mirror of
https://github.com/ehw-fit/ariths-gen.git
synced 2025-04-20 13:51:23 +01:00
Implemented CSA and Wallace tree multiplier composing of CSAs. Also did some code cleanup.
This commit is contained in:
parent
3c47407f80
commit
6f05db002e
@ -1,5 +1,6 @@
|
|||||||
from .arithmetic_circuit import (
|
from .arithmetic_circuit import (
|
||||||
ArithmeticCircuit
|
ArithmeticCircuit,
|
||||||
|
ThreeInputArithmeticCircuit
|
||||||
)
|
)
|
||||||
|
|
||||||
from .general_circuit import (GeneralCircuit )
|
from .general_circuit import (GeneralCircuit )
|
||||||
|
@ -38,9 +38,6 @@ class ArithmeticCircuit(GeneralCircuit):
|
|||||||
self.a = Bus(prefix=f"{a.prefix}", wires_list=a.bus)
|
self.a = Bus(prefix=f"{a.prefix}", wires_list=a.bus)
|
||||||
self.b = Bus(prefix=f"{b.prefix}", wires_list=b.bus)
|
self.b = Bus(prefix=f"{b.prefix}", wires_list=b.bus)
|
||||||
|
|
||||||
# N output wires for given circuit
|
|
||||||
self.out = Bus(self.prefix+"_out", out_N, out_bus=True, signed=self.signed)
|
|
||||||
|
|
||||||
""" C CODE GENERATION """
|
""" C CODE GENERATION """
|
||||||
def get_prototype_c(self):
|
def get_prototype_c(self):
|
||||||
"""Generates C code function header to describe corresponding arithmetic circuit's interface in C code.
|
"""Generates C code function header to describe corresponding arithmetic circuit's interface in C code.
|
||||||
@ -76,3 +73,193 @@ class ArithmeticCircuit(GeneralCircuit):
|
|||||||
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||||
f".names vdd\n1\n" + \
|
f".names vdd\n1\n" + \
|
||||||
f".names gnd\n0\n"
|
f".names gnd\n0\n"
|
||||||
|
|
||||||
|
|
||||||
|
class ThreeInputArithmeticCircuit(GeneralCircuit):
|
||||||
|
"""Class represents a general three input arithmetic circuit and ensures its generation to various representations.
|
||||||
|
|
||||||
|
The __init__ method fills some mandatory attributes concerning arithmetic circuit
|
||||||
|
that are later used for generation into various representations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, a, b, c, prefix: str, name: str, out_N: int, inner_component: bool = False, one_bit_circuit: bool = False, signed: bool = False, **kwargs):
|
||||||
|
super().__init__(prefix, name, out_N, inner_component, inputs=[a, b, c], signed=signed, **kwargs)
|
||||||
|
if one_bit_circuit is False:
|
||||||
|
if prefix == "":
|
||||||
|
self.prefix = name
|
||||||
|
else:
|
||||||
|
self.prefix = prefix + "_" + name
|
||||||
|
|
||||||
|
self.inner_component = inner_component
|
||||||
|
if self.inner_component is True:
|
||||||
|
self.a = Bus(prefix=f"{self.prefix}_{a.prefix}", wires_list=a.bus)
|
||||||
|
self.b = Bus(prefix=f"{self.prefix}_{b.prefix}", wires_list=b.bus)
|
||||||
|
self.c = Bus(prefix=f"{self.prefix}_{c.prefix}", wires_list=c.bus)
|
||||||
|
|
||||||
|
if a.is_output_bus():
|
||||||
|
self.a.connect_bus(connecting_bus=a)
|
||||||
|
if b.is_output_bus():
|
||||||
|
self.b.connect_bus(connecting_bus=b)
|
||||||
|
if c.is_output_bus():
|
||||||
|
self.c.connect_bus(connecting_bus=c)
|
||||||
|
else:
|
||||||
|
self.a = Bus(prefix=f"{a.prefix}", wires_list=a.bus)
|
||||||
|
self.b = Bus(prefix=f"{b.prefix}", wires_list=b.bus)
|
||||||
|
self.c = Bus(prefix=f"{c.prefix}", wires_list=c.bus)
|
||||||
|
|
||||||
|
""" C CODE GENERATION """
|
||||||
|
def get_prototype_c(self):
|
||||||
|
"""Generates C code function header to describe corresponding arithmetic circuit's interface in C code.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Function's name and parameters in C code.
|
||||||
|
"""
|
||||||
|
return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.prefix}, {self.c_data_type} {self.b.prefix}, {self.c_data_type} {self.c.prefix})" + "{" + "\n"
|
||||||
|
|
||||||
|
def get_function_block_c(self):
|
||||||
|
"""Generates hierarchical C code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Hierarchical C code of multi-bit arithmetic circuit's function block description.
|
||||||
|
"""
|
||||||
|
# Obtain proper circuit name with its bit width
|
||||||
|
circuit_prefix = self.__class__(
|
||||||
|
a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||||
|
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||||
|
N=self.N, prefix="b"), c=Bus(
|
||||||
|
N=self.N, prefix="c"), name=circuit_prefix)
|
||||||
|
return f"{circuit_block.get_circuit_c()}\n\n"
|
||||||
|
|
||||||
|
def get_declaration_c_hier(self):
|
||||||
|
"""Generates hierarchical C code declaration of corresponding subcomponent input/output wires inside the upper component.
|
||||||
|
|
||||||
|
Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present
|
||||||
|
inside the upper component from which function block has been invoked.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Hierarchical C code of subcomponent arithmetic circuit's wires declaration.
|
||||||
|
"""
|
||||||
|
return f" {self.c_data_type} {self.a.prefix} = 0;\n" + \
|
||||||
|
f" {self.c_data_type} {self.b.prefix} = 0;\n" + \
|
||||||
|
f" {self.c_data_type} {self.c.prefix} = 0;\n" + \
|
||||||
|
f" {self.c_data_type} {self.out.prefix} = 0;\n"
|
||||||
|
|
||||||
|
def get_out_invocation_c(self):
|
||||||
|
"""Generates hierarchical C code invocation of corresponding arithmetic circuit's generated function block.
|
||||||
|
|
||||||
|
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
|
||||||
|
Assigns output values from invocation of the corresponding function block into inner wires present inside
|
||||||
|
the upper component from which function block has been invoked.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Hierarchical C code of subcomponent's C function invocation and output assignment.
|
||||||
|
"""
|
||||||
|
# Getting name of circuit type for proper C code generation without affecting actual generated composition
|
||||||
|
circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||||
|
return self.a.return_bus_wires_values_c_hier() + self.b.return_bus_wires_values_c_hier() + self.c.return_bus_wires_values_c_hier() + \
|
||||||
|
f" {self.out.prefix} = {circuit_type}({self.a.prefix}, {self.b.prefix}, {self.c.prefix});\n"
|
||||||
|
|
||||||
|
""" VERILOG CODE GENERATION """
|
||||||
|
def get_prototype_v(self):
|
||||||
|
"""Generates Verilog code module header to describe corresponding arithmetic circuit's interface in Verilog code.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Module's name and parameters in Verilog code.
|
||||||
|
"""
|
||||||
|
return f"module {self.prefix}(input [{self.N-1}:0] {self.a.prefix}, input [{self.N-1}:0] {self.b.prefix}, input [{self.N-1}:0] {self.c.prefix}, output [{self.out.N-1}:0] {self.out.prefix});\n"
|
||||||
|
|
||||||
|
def get_function_block_v(self):
|
||||||
|
"""Generates hierarchical Verilog code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Hierarchical Verilog code of multi-bit arithmetic circuit's function block description.
|
||||||
|
"""
|
||||||
|
# Obtain proper circuit name with its bit width
|
||||||
|
circuit_prefix = self.__class__(
|
||||||
|
a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||||
|
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||||
|
N=self.N, prefix="b"), c=Bus(N=self.N, prefix="c"), name=circuit_prefix)
|
||||||
|
return f"{circuit_block.get_circuit_v()}\n\n"
|
||||||
|
|
||||||
|
def get_declaration_v_hier(self):
|
||||||
|
"""Generates hierarchical Verilog code declaration of corresponding subcomponent input/output wires inside the upper component.
|
||||||
|
|
||||||
|
Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present
|
||||||
|
inside the upper component from which function block has been invoked.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Hierarchical Verilog code of subcomponent arithmetic circuit's wires declaration.
|
||||||
|
"""
|
||||||
|
return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \
|
||||||
|
f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \
|
||||||
|
f" wire [{self.c.N-1}:0] {self.c.prefix};\n" + \
|
||||||
|
f" wire [{self.out.N-1}:0] {self.out.prefix};\n"
|
||||||
|
|
||||||
|
def get_out_invocation_v(self):
|
||||||
|
"""Generates hierarchical Verilog code invocation of corresponding arithmetic circuit's generated function block.
|
||||||
|
|
||||||
|
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
|
||||||
|
Assigns output values from invocation of the corresponding function block into inner wires present inside
|
||||||
|
the upper component from which function block has been invoked.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Hierarchical Verilog code of subcomponent's module invocation and output assignment.
|
||||||
|
"""
|
||||||
|
# Getting name of circuit type and insitu copying out bus for proper Verilog code generation without affecting actual generated composition
|
||||||
|
circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||||
|
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||||
|
N=self.N, prefix="b"), c=Bus(
|
||||||
|
N=self.N, prefix="c"), name=circuit_type)
|
||||||
|
return self.a.return_bus_wires_values_v_hier() + self.b.return_bus_wires_values_v_hier() + self.c.return_bus_wires_values_v_hier() + \
|
||||||
|
f" {circuit_type} {circuit_type}_{self.out.prefix}(.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.c.prefix}({self.c.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n"
|
||||||
|
|
||||||
|
""" BLIF CODE GENERATION """
|
||||||
|
def get_declaration_blif(self):
|
||||||
|
"""Generates flat Blif code declaration of input/output circuit wires.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Flat Blif code containing declaration of circuit's wires.
|
||||||
|
"""
|
||||||
|
if self.N == 1:
|
||||||
|
return f".inputs {self.a.prefix} {self.b.prefix} {self.c.prefix}\n" + \
|
||||||
|
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||||
|
f".names vdd\n1\n" + \
|
||||||
|
f".names gnd\n0\n"
|
||||||
|
else:
|
||||||
|
return f".inputs{self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}{self.c.get_wire_declaration_blif()}\n" + \
|
||||||
|
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||||
|
f".names vdd\n1\n" + \
|
||||||
|
f".names gnd\n0\n"
|
||||||
|
|
||||||
|
def get_invocation_blif_hier(self):
|
||||||
|
"""Generates hierarchical Blif code invocation of corresponding arithmetic circuit's generated function block.
|
||||||
|
|
||||||
|
Used for multi-bit subcomponent's modul invocation.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Hierarchical Blif code of subcomponent's model invocation and output assignment.
|
||||||
|
"""
|
||||||
|
# Getting name of circuit type for proper Blif code generation without affecting actual generated composition
|
||||||
|
circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||||
|
return f"{self.a.get_wire_assign_blif(output=True)}" + \
|
||||||
|
f"{self.b.get_wire_assign_blif(output=True)}" + \
|
||||||
|
f"{self.c.get_wire_assign_blif(output=True)}" + \
|
||||||
|
f".subckt {circuit_type}" + \
|
||||||
|
"".join([f" a[{self.a.bus.index(w)}]={self.a.prefix}[{self.a.bus.index(w)}]" for w in self.a.bus]) + \
|
||||||
|
"".join([f" b[{self.b.bus.index(w)}]={self.b.prefix}[{self.b.bus.index(w)}]" for w in self.b.bus]) + \
|
||||||
|
"".join([f" c[{self.c.bus.index(w)}]={self.c.prefix}[{self.c.bus.index(w)}]" for w in self.c.bus]) + \
|
||||||
|
"".join(
|
||||||
|
[f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n"
|
||||||
|
|
||||||
|
def get_function_block_blif(self):
|
||||||
|
"""Generates hierarchical Blif code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Hierarchical Blif code of multi-bit arithmetic circuit's function block description.
|
||||||
|
"""
|
||||||
|
# Obtain proper circuit name with its bit width
|
||||||
|
circuit_prefix = self.__class__(
|
||||||
|
a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||||
|
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||||
|
N=self.N, prefix="b"), c=Bus(N=self.N, prefix="c"), name=circuit_prefix)
|
||||||
|
return f"{circuit_block.get_circuit_blif()}"
|
||||||
|
@ -9,6 +9,7 @@ from ariths_gen.wire_components import (
|
|||||||
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
|
|
||||||
class GeneralCircuit():
|
class GeneralCircuit():
|
||||||
"""Class represents a general circuit and ensures its generation to various representations.
|
"""Class represents a general circuit and ensures its generation to various representations.
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ class GeneralCircuit():
|
|||||||
that are later used for generation into various representations.
|
that are later used for generation into various representations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list=[], signed: bool = False, outname = None):
|
def __init__(self, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list = [], signed: bool = False, outname=None):
|
||||||
if prefix == "":
|
if prefix == "":
|
||||||
self.prefix = name
|
self.prefix = name
|
||||||
else:
|
else:
|
||||||
@ -32,7 +33,7 @@ class GeneralCircuit():
|
|||||||
self.circuit_gates = []
|
self.circuit_gates = []
|
||||||
self.c_data_type = "uint64_t"
|
self.c_data_type = "uint64_t"
|
||||||
self.signed = signed
|
self.signed = signed
|
||||||
self.pyc = None # Python compiled function
|
self.pyc = None # Python compiled function
|
||||||
|
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
if not self.pyc:
|
if not self.pyc:
|
||||||
@ -41,7 +42,6 @@ class GeneralCircuit():
|
|||||||
|
|
||||||
globs, locs = {}, {}
|
globs, locs = {}, {}
|
||||||
exec(buf.getvalue(), globs, locs)
|
exec(buf.getvalue(), globs, locs)
|
||||||
#print(locs)
|
|
||||||
self.pyc = locs[self.prefix]
|
self.pyc = locs[self.prefix]
|
||||||
|
|
||||||
return self.pyc(*args)
|
return self.pyc(*args)
|
||||||
@ -208,6 +208,9 @@ class GeneralCircuit():
|
|||||||
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.a.bus]
|
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.a.bus]
|
||||||
[self.circuit_wires.append(
|
[self.circuit_wires.append(
|
||||||
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.b.bus]
|
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.b.bus]
|
||||||
|
if hasattr(self, 'c'):
|
||||||
|
[self.circuit_wires.append(
|
||||||
|
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.c.bus]
|
||||||
else:
|
else:
|
||||||
self.circuit_wires.append(
|
self.circuit_wires.append(
|
||||||
(self.a, f"{self.a.name}", self.save_wire_id(wire=self.a)))
|
(self.a, f"{self.a.name}", self.save_wire_id(wire=self.a)))
|
||||||
@ -397,18 +400,15 @@ class GeneralCircuit():
|
|||||||
Returns:
|
Returns:
|
||||||
str: Hierarchical C code initialization of arithmetic circuit wires.
|
str: Hierarchical C code initialization of arithmetic circuit wires.
|
||||||
"""
|
"""
|
||||||
return "".join([c.get_gate_invocation_c() if isinstance(c, TwoInputLogicGate) else c.get_out_invocation_c(circuit_prefix=self.prefix) for c in self.components])
|
return "".join([c.get_gate_invocation_c() if isinstance(c, TwoInputLogicGate) else c.get_out_invocation_c() for c in self.components])
|
||||||
|
|
||||||
def get_out_invocation_c(self, circuit_prefix: str):
|
def get_out_invocation_c(self):
|
||||||
"""Generates hierarchical C code invocation of corresponding arithmetic circuit's generated function block.
|
"""Generates hierarchical C code invocation of corresponding arithmetic circuit's generated function block.
|
||||||
|
|
||||||
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
|
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
|
||||||
Assigns output values from invocation of the corresponding function block into inner wires present inside
|
Assigns output values from invocation of the corresponding function block into inner wires present inside
|
||||||
the upper component from which function block has been invoked.
|
the upper component from which function block has been invoked.
|
||||||
|
|
||||||
Args:
|
|
||||||
circuit_prefix (str): Prefix name of the upper component from which function block is being invoked.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: Hierarchical C code of subcomponent's C function invocation and output assignment.
|
str: Hierarchical C code of subcomponent's C function invocation and output assignment.
|
||||||
"""
|
"""
|
||||||
@ -549,18 +549,15 @@ class GeneralCircuit():
|
|||||||
Returns:
|
Returns:
|
||||||
str: Hierarchical Verilog code initialization of arithmetic circuit wires.
|
str: Hierarchical Verilog code initialization of arithmetic circuit wires.
|
||||||
"""
|
"""
|
||||||
return "".join([c.get_gate_invocation_v() if isinstance(c, TwoInputLogicGate) else c.get_out_invocation_v(circuit_prefix=self.prefix) for c in self.components])
|
return "".join([c.get_gate_invocation_v() if isinstance(c, TwoInputLogicGate) else c.get_out_invocation_v() for c in self.components])
|
||||||
|
|
||||||
def get_out_invocation_v(self, circuit_prefix: str):
|
def get_out_invocation_v(self):
|
||||||
"""Generates hierarchical Verilog code invocation of corresponding arithmetic circuit's generated function block.
|
"""Generates hierarchical Verilog code invocation of corresponding arithmetic circuit's generated function block.
|
||||||
|
|
||||||
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
|
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
|
||||||
Assigns output values from invocation of the corresponding function block into inner wires present inside
|
Assigns output values from invocation of the corresponding function block into inner wires present inside
|
||||||
the upper component from which function block has been invoked.
|
the upper component from which function block has been invoked.
|
||||||
|
|
||||||
Args:
|
|
||||||
circuit_prefix (str): Prefix name of the upper component from which function block is being invoked.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: Hierarchical Verilog code of subcomponent's module invocation and output assignment.
|
str: Hierarchical Verilog code of subcomponent's module invocation and output assignment.
|
||||||
"""
|
"""
|
||||||
@ -659,16 +656,13 @@ class GeneralCircuit():
|
|||||||
Returns:
|
Returns:
|
||||||
str: Hierarchical Blif code containing invocations of inner subcomponents function blocks.
|
str: Hierarchical Blif code containing invocations of inner subcomponents function blocks.
|
||||||
"""
|
"""
|
||||||
return "".join(c.get_invocation_blif_hier(circuit_prefix=self.prefix) for c in self.components)
|
return "".join(c.get_invocation_blif_hier() for c in self.components)
|
||||||
|
|
||||||
def get_invocation_blif_hier(self, circuit_prefix: str):
|
def get_invocation_blif_hier(self):
|
||||||
"""Generates hierarchical Blif code invocation of corresponding arithmetic circuit's generated function block.
|
"""Generates hierarchical Blif code invocation of corresponding arithmetic circuit's generated function block.
|
||||||
|
|
||||||
Used for multi-bit subcomponent's modul invocation.
|
Used for multi-bit subcomponent's modul invocation.
|
||||||
|
|
||||||
Args:
|
|
||||||
circuit_prefix (str): Prefix name of the upper component from which function block is being invoked.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: Hierarchical Blif code of subcomponent's model invocation and output assignment.
|
str: Hierarchical Blif code of subcomponent's model invocation and output assignment.
|
||||||
"""
|
"""
|
||||||
@ -741,7 +735,7 @@ class GeneralCircuit():
|
|||||||
str: CGP chromosome parameters of described arithmetic circuit.
|
str: CGP chromosome parameters of described arithmetic circuit.
|
||||||
"""
|
"""
|
||||||
self.circuit_gates = self.get_circuit_gates()
|
self.circuit_gates = self.get_circuit_gates()
|
||||||
return f"{{{self.a.N+self.a.N},{self.out.N},1,{len(self.circuit_gates)},2,1,0}}"
|
return f"{{{sum(input_bus.N for input_bus in self.inputs)},{self.out.N},1,{len(self.circuit_gates)},2,1,0}}"
|
||||||
|
|
||||||
def get_triplets_cgp(self):
|
def get_triplets_cgp(self):
|
||||||
"""Generates list of logic gate triplets (first input wire, second input wire, logic gate function) using wires unique position indexes within the described circuit.
|
"""Generates list of logic gate triplets (first input wire, second input wire, logic gate function) using wires unique position indexes within the described circuit.
|
||||||
|
@ -46,7 +46,7 @@ class MultiplierCircuit(ArithmeticCircuit):
|
|||||||
"""
|
"""
|
||||||
# To get the index of previous row's connecting adder and its generated pp
|
# To get the index of previous row's connecting adder and its generated pp
|
||||||
if mult_type == "bam":
|
if mult_type == "bam":
|
||||||
#TODO alter to be more compact
|
# TODO alter to be more compact
|
||||||
ids_sum = 0
|
ids_sum = 0
|
||||||
for row in range(self.horizontal_cut + self.ommited_rows, b_index):
|
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_row_elem_id = self.vertical_cut-row if self.vertical_cut-row > 0 else 0
|
||||||
@ -65,8 +65,6 @@ class MultiplierCircuit(ArithmeticCircuit):
|
|||||||
else:
|
else:
|
||||||
index = ((b_index-2) * ((self.N)*2)) + ((self.N-1)+2*(a_index+2))
|
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
|
# Get carry wire as input for the last adder in current row
|
||||||
if a_index == self.N-1:
|
if a_index == self.N-1:
|
||||||
index = index-2
|
index = index-2
|
||||||
@ -103,26 +101,71 @@ class MultiplierCircuit(ArithmeticCircuit):
|
|||||||
if d >= initial_value:
|
if d >= initial_value:
|
||||||
return stage, max_height
|
return stage, max_height
|
||||||
|
|
||||||
def init_column_heights(self, signed: bool = False):
|
def init_row_lengths(self):
|
||||||
"""Creates appropriate number of partial product columns along with filling them with corresponding number of bit pairs.
|
"""Creates appropriate number of partial product rows along with filling them with corresponding number of bit pairs.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: List of partial product rows with their bit pairs.
|
||||||
|
"""
|
||||||
|
rows = [[] for _ in range(self.N)]
|
||||||
|
rows = [self.add_row_wires(row=row, row_index=rows.index(row)) for row in rows]
|
||||||
|
return rows
|
||||||
|
|
||||||
|
def add_row_wires(self, row: list, row_index: int):
|
||||||
|
"""Fills circuit's partial product row with corresponding bit pairs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
signed (bool, optional): Specify whether pp columns bit pairs should perform signed multiplication or not. Defaults to False.
|
row (list): List representing row of partial product bits.
|
||||||
|
row_index (int): Index of partial products row.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: Updated row list containing corresponding number of input bit pairs to form proper pp row.
|
||||||
|
"""
|
||||||
|
# Number of partial products present in the row (should be equal to circuit's input bus size)
|
||||||
|
row_pp_count = self.N
|
||||||
|
# Adding neccessary number of lists (based on number of bits in the row – stored in `row_pp_count`)
|
||||||
|
# to row that each represent individual bit pairs for described row (these bit pairs are then combined in AND/NAND gates)
|
||||||
|
[row.append([]) for _ in range(row_pp_count)]
|
||||||
|
|
||||||
|
# Filling row bit pair lists with appropriate bits
|
||||||
|
[row[index].append(self.a.get_wire(index)) for index in range(row_pp_count)]
|
||||||
|
[row[index].append(self.b.get_wire(row_index)) for index in range(row_pp_count)]
|
||||||
|
|
||||||
|
# Converting unsigned rows of pp bit pair lists into AND gates
|
||||||
|
if self.signed is False:
|
||||||
|
row[0:] = [self.add_component(AndGate(a=row[i][0], b=row[i][1], prefix=self.prefix+'_and_'+str(row[i][0].index)+'_'+str(row[i][1].index), parent_component=self)).out for i in range(row_pp_count)]
|
||||||
|
# Converting signed rows of pp bit pair lists into AND/NAND gates (based on Baugh-Wooley multiplication algorithm)
|
||||||
|
else:
|
||||||
|
# Partial product bit pairs of all rows (expect for the last one) are connected to AND gates, besides the last pp bit pair in each row that is connected to a NAND gate
|
||||||
|
if row_index != self.N-1:
|
||||||
|
row[0:row_pp_count-1] = [self.add_component(AndGate(a=row[i][0], b=row[i][1], prefix=self.prefix+'_and_'+str(row[i][0].index)+'_'+str(row[i][1].index), parent_component=self)).out for i in range(row_pp_count-1)]
|
||||||
|
|
||||||
|
row[row_pp_count-1] = self.add_component(NandGate(a=row[row_pp_count-1][0], b=row[row_pp_count-1][1], prefix=self.prefix+'_nand_'+str(row[row_pp_count-1][0].index)+'_'+str(row[row_pp_count-1][1].index), parent_component=self)).out
|
||||||
|
# Partial product bit pairs of the last row are connected to NAND gates besides the last pp pair that is connected to an AND gate
|
||||||
|
else:
|
||||||
|
row[0:row_pp_count-1] = [self.add_component(NandGate(a=row[i][0], b=row[i][1], prefix=self.prefix+'_nand_'+str(row[i][0].index)+'_'+str(row[i][1].index), parent_component=self)).out for i in range(row_pp_count-1)]
|
||||||
|
|
||||||
|
row[row_pp_count-1] = self.add_component(AndGate(a=row[row_pp_count-1][0], b=row[row_pp_count-1][1], prefix=self.prefix+'_and_'+str(row[row_pp_count-1][0].index)+'_'+str(row[row_pp_count-1][1].index), parent_component=self)).out
|
||||||
|
|
||||||
|
pp_row_wires = Bus(prefix=f"pp_row{row_index}", wires_list=row)
|
||||||
|
return pp_row_wires
|
||||||
|
|
||||||
|
def init_column_heights(self):
|
||||||
|
"""Creates appropriate number of partial product columns along with filling them with corresponding number of bit pairs.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: List of partial product columns with their bit pairs.
|
list: List of partial product columns with their bit pairs.
|
||||||
"""
|
"""
|
||||||
columns = [[num] if num <= self.N else [num - (num - self.N)*2] for num in range(1, self.out.N)]
|
columns = [[num] if num <= self.N else [num - (num - self.N)*2] for num in range(1, self.out.N)]
|
||||||
columns = [self.add_column_wires(column=col, column_index=columns.index(col), signed=signed) for col in columns]
|
columns = [self.add_column_wires(column=col, column_index=columns.index(col)) for col in columns]
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
def add_column_wires(self, column: list, column_index: int, signed: bool):
|
def add_column_wires(self, column: list, column_index: int):
|
||||||
"""Fills circuit's partial product column with corresponding bit pairs.
|
"""Fills circuit's partial product column with corresponding bit pairs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
column (list): List representing column of partial product bits.
|
column (list): List representing column of partial product bits.
|
||||||
column_index (int): Index of partial products column.
|
column_index (int): Index of partial products column.
|
||||||
signed (bool): Specify whether pp columns bit pairs should perform signed multiplication or not.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: Updated column list containing corresponding number of input bit pairs to form proper pp column.
|
list: Updated column list containing corresponding number of input bit pairs to form proper pp column.
|
||||||
@ -139,7 +182,7 @@ class MultiplierCircuit(ArithmeticCircuit):
|
|||||||
[column[index-(self.a.N-1-column[0])].append(self.b.get_wire(index)) for index in range(self.a.N-column[0], self.a.N)]
|
[column[index-(self.a.N-1-column[0])].append(self.b.get_wire(index)) for index in range(self.a.N-column[0], self.a.N)]
|
||||||
|
|
||||||
# Converting unsigned column pp bit pair lists into AND gates
|
# Converting unsigned column pp bit pair lists into AND gates
|
||||||
if signed is False:
|
if self.signed is False:
|
||||||
column[1:] = [AndGate(a=column[i][0], b=column[i][1], prefix=self.prefix+'_and_'+str(column[i][0].index)+'_'+str(column[i][1].index), parent_component=self) for i in range(1, len(column))]
|
column[1:] = [AndGate(a=column[i][0], b=column[i][1], prefix=self.prefix+'_and_'+str(column[i][0].index)+'_'+str(column[i][1].index), parent_component=self) for i in range(1, len(column))]
|
||||||
# Converting signed column pp bit pair lists into AND/NAND gates (based on Baugh-Wooley multiplication algorithm)
|
# Converting signed column pp bit pair lists into AND/NAND gates (based on Baugh-Wooley multiplication algorithm)
|
||||||
else:
|
else:
|
||||||
|
@ -17,3 +17,8 @@ from ariths_gen.multi_bit_circuits.adders.carry_skip_adder import (
|
|||||||
UnsignedCarrySkipAdder,
|
UnsignedCarrySkipAdder,
|
||||||
SignedCarrySkipAdder
|
SignedCarrySkipAdder
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from ariths_gen.multi_bit_circuits.adders.carry_save_adder import (
|
||||||
|
CarrySaveAdderComponent,
|
||||||
|
UnsignedCarrySaveAdder
|
||||||
|
)
|
||||||
|
155
ariths_gen/multi_bit_circuits/adders/carry_save_adder.py
Normal file
155
ariths_gen/multi_bit_circuits/adders/carry_save_adder.py
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
from ariths_gen.wire_components import (
|
||||||
|
Wire,
|
||||||
|
ConstantWireValue0,
|
||||||
|
ConstantWireValue1,
|
||||||
|
Bus
|
||||||
|
)
|
||||||
|
from ariths_gen.core.arithmetic_circuits import (
|
||||||
|
ArithmeticCircuit,
|
||||||
|
ThreeInputArithmeticCircuit,
|
||||||
|
MultiplierCircuit
|
||||||
|
)
|
||||||
|
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||||
|
HalfAdder,
|
||||||
|
PGLogicBlock,
|
||||||
|
FullAdder,
|
||||||
|
FullAdderPG
|
||||||
|
)
|
||||||
|
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||||
|
AndGate,
|
||||||
|
NandGate,
|
||||||
|
OrGate,
|
||||||
|
NorGate,
|
||||||
|
XorGate,
|
||||||
|
XnorGate,
|
||||||
|
NotGate
|
||||||
|
)
|
||||||
|
from ariths_gen.multi_bit_circuits.adders import (
|
||||||
|
UnsignedCarryLookaheadAdder,
|
||||||
|
UnsignedPGRippleCarryAdder,
|
||||||
|
UnsignedRippleCarryAdder,
|
||||||
|
SignedCarryLookaheadAdder,
|
||||||
|
SignedPGRippleCarryAdder,
|
||||||
|
SignedRippleCarryAdder
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CarrySaveAdderComponent(ThreeInputArithmeticCircuit):
|
||||||
|
"""Class representing carry save adder component.
|
||||||
|
|
||||||
|
The carry save adder component is especially useful when constructing tree multiplier architectures to reduce the propagation delay as opposed to traditional implementation of tree multipliers with half/full adders.
|
||||||
|
|
||||||
|
The circuit is composed of full adders that operate paralelly and each take 3 input operands.
|
||||||
|
The final output bus is composed of sum and carry bits of its corresponding contained full adders.
|
||||||
|
```
|
||||||
|
C3 B3 A3 C2 B2 A2 C1 B1 A1 C0 B0 A0
|
||||||
|
│ │ │ │ │ │ │ │ │ │ │ │
|
||||||
|
┌▼─▼─▼─┐ ┌▼─▼─▼─┐ ┌▼─▼─▼─┐ ┌▼─▼─▼─┐
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
│ FA │ │ FA │ │ FA │ │ FA │
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
└─┬──┬─┘ └─┬──┬─┘ └─┬──┬─┘ └─┬──┬─┘
|
||||||
|
│c3│s3 │c2│s2 │c1│s1 │c0│s0
|
||||||
|
└──┼──┐ ┌─┘ │┌──────┼──┼───────┘ │
|
||||||
|
│ │ │ ┌─┼┼──────┘ └───────┐ │
|
||||||
|
│ │ │ │ └┼──────────────┐ │ │
|
||||||
|
└──┼──┼──┼──┼───────────┐ │ │ │
|
||||||
|
│ │ │ │ 0 0/1│ │ │ │
|
||||||
|
│ │ │ │ │ │ │ │ │ │
|
||||||
|
o9 o8 o7 o6 o5 o4 o3 o2 o1 o0
|
||||||
|
c3 c2 c1 c0 0 0/1 s3 s2 s1 s0
|
||||||
|
```
|
||||||
|
|
||||||
|
Description of the __init__ method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
a (Bus): First input bus.
|
||||||
|
b (Bus): Second input bus.
|
||||||
|
c (Bus): Third input bus.
|
||||||
|
prefix (str, optional): Prefix name of csa component. Defaults to "".
|
||||||
|
name (str, optional): Name of csa component. Defaults to "csa_component".
|
||||||
|
signed (bool, optional): Specifies whether the component should be used in (un)signed context (used during the construction of tree multipliers). Defaults to False.
|
||||||
|
"""
|
||||||
|
def __init__(self, a: Bus, b: Bus, c: Bus, prefix: str = "", name: str = "csa_component", signed: bool = False, **kwargs):
|
||||||
|
self.N = max(a.N, b.N, c.N)
|
||||||
|
super().__init__(a=a, b=b, c=c, prefix=prefix, name=name, out_N=(2*self.N)+2, signed=signed, **kwargs)
|
||||||
|
|
||||||
|
bus_extension_wire = ConstantWireValue1() if self.signed is True else ConstantWireValue0()
|
||||||
|
self.a.bus_extend(N=self.N, prefix=a.prefix, desired_extension_wire=bus_extension_wire)
|
||||||
|
self.b.bus_extend(N=self.N, prefix=b.prefix, desired_extension_wire=bus_extension_wire)
|
||||||
|
self.c.bus_extend(N=self.N, prefix=c.prefix, desired_extension_wire=bus_extension_wire)
|
||||||
|
|
||||||
|
self.sum_bits = Bus(prefix=self.prefix + "_sums", N=int(self.out.N/2))
|
||||||
|
self.carry_bits = Bus(prefix=self.prefix + "_carries", N=int(self.out.N/2))
|
||||||
|
self.sum_bits.connect(-1, bus_extension_wire)
|
||||||
|
self.carry_bits.connect(0, ConstantWireValue0())
|
||||||
|
|
||||||
|
# Gradual addition of 1-bit CSA components (FAs are used)
|
||||||
|
for input_index in range(self.N):
|
||||||
|
obj_adder = FullAdder(self.a.get_wire(input_index), self.b.get_wire(input_index), self.c.get_wire(input_index), prefix=self.prefix+"_fa"+str(input_index))
|
||||||
|
self.add_component(obj_adder)
|
||||||
|
|
||||||
|
self.sum_bits.connect(input_index, obj_adder.get_sum_wire())
|
||||||
|
self.carry_bits.connect(input_index+1, obj_adder.get_carry_wire())
|
||||||
|
|
||||||
|
[self.out.connect(o, self.sum_bits.get_wire(o)) for o in range(0, int(self.out.N/2))]
|
||||||
|
[self.out.connect(o, self.carry_bits.get_wire(o-int(self.out.N/2))) for o in range(int(self.out.N/2), self.out.N)]
|
||||||
|
|
||||||
|
|
||||||
|
class UnsignedCarrySaveAdder(ThreeInputArithmeticCircuit):
|
||||||
|
"""Class representing unsigned carry save adder.
|
||||||
|
|
||||||
|
Unsigned carry save adder represents 3 input N-bit unsigned adder which is composed of
|
||||||
|
N one bit full adders, that are not interconnected as in for example the ripple carry adder,
|
||||||
|
but instead operate parallelly.
|
||||||
|
|
||||||
|
The sum and carry bits of the individual full adders are connected to a multi bit adder to sum the final result.
|
||||||
|
```
|
||||||
|
C3 B3 A3 C2 B2 A2 C1 B1 A1 C0 B0 A0
|
||||||
|
│ │ │ │ │ │ │ │ │ │ │ │
|
||||||
|
┌▼─▼─▼─┐ ┌▼─▼─▼─┐ ┌▼─▼─▼─┐ ┌▼─▼─▼─┐
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
│ FA │ │ FA │ │ FA │ │ FA │
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
└─┬──┬─┘ └─┬──┬─┘ └─┬──┬─┘ └─┬──┬─┘
|
||||||
|
┌┘c3│s3┌────┘c2│s2┌────┘c1│s1┌────┘c0│s0
|
||||||
|
0 │ │ │ │ │ │ │ │ 0
|
||||||
|
│Y4│X4 │Y3│X3 │Y2│X2 │Y1│X1 │Y0│X0
|
||||||
|
┌─▼──▼───▼──▼───────▼──▼───────▼──▼───────▼──▼─┐
|
||||||
|
│ 5-bit multi bit adder │
|
||||||
|
│ │
|
||||||
|
└┬────────┬────────┬────────┬────────┬────────┬┘
|
||||||
|
Cout S4 S3 S2 S1 S0
|
||||||
|
```
|
||||||
|
|
||||||
|
Description of the __init__ method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
a (Bus): First input bus.
|
||||||
|
b (Bus): Second input bus.
|
||||||
|
c (Bus): Third input bus.
|
||||||
|
prefix (str, optional): Prefix name of unsigned csa. Defaults to "".
|
||||||
|
name (str, optional): Name of unsigned csa. Defaults to "u_csa".
|
||||||
|
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sum. Defaults to UnsignedCarryLookaheadAdder.
|
||||||
|
"""
|
||||||
|
def __init__(self, a: Bus, b: Bus, c: Bus, prefix: str = "", name: str = "u_csa", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||||
|
self.N = max(a.N, b.N, c.N)
|
||||||
|
super().__init__(a=a, b=b, c=c, prefix=prefix, name=name, out_N=self.N+2, **kwargs)
|
||||||
|
|
||||||
|
# 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.c.bus_extend(N=self.N, prefix=c.prefix)
|
||||||
|
|
||||||
|
csa_adder = CarrySaveAdderComponent(a=self.a, b=self.b, c=self.c, prefix=prefix, name="csa_comp" + str(self.N), inner_component=True)
|
||||||
|
self.add_component(csa_adder)
|
||||||
|
|
||||||
|
# Obtain proper adder name with its bit width
|
||||||
|
adder_name = unsigned_adder_class_name(a=self.a, b=self.b).prefix + str(self.N+1)
|
||||||
|
adder_a = Bus(prefix="a", N=csa_adder.sum_bits.N)
|
||||||
|
adder_b = Bus(prefix="b", N=csa_adder.carry_bits.N)
|
||||||
|
adder_a.connect_bus(connecting_bus=csa_adder.out, end_connection_pos=int(csa_adder.out.N/2))
|
||||||
|
adder_b.connect_bus(connecting_bus=csa_adder.out, start_connection_pos=int(csa_adder.out.N/2), end_connection_pos=csa_adder.out.N, offset=int(csa_adder.out.N/2))
|
||||||
|
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True)
|
||||||
|
self.add_component(final_adder)
|
||||||
|
self.out.connect_bus(connecting_bus=final_adder.out)
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
from ariths_gen.wire_components import (
|
from ariths_gen.wire_components import (
|
||||||
Wire,
|
Wire,
|
||||||
ConstantWireValue0,
|
ConstantWireValue0,
|
||||||
@ -27,15 +26,13 @@ from ariths_gen.one_bit_circuits.logic_gates import (
|
|||||||
XnorGate,
|
XnorGate,
|
||||||
NotGate
|
NotGate
|
||||||
)
|
)
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
class UnsignedCGPCircuit(GeneralCircuit):
|
class UnsignedCGPCircuit(GeneralCircuit):
|
||||||
""" Circuit that loads CGP code and is able to export it to C/verilog/Blif/CGP """
|
"""Unsigned circuit variant that loads CGP code and is able to export it to C/verilog/Blif/CGP."""
|
||||||
|
|
||||||
def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs):
|
def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs):
|
||||||
|
|
||||||
cgp_prefix, cgp_core, cgp_outputs = re.match(
|
cgp_prefix, cgp_core, cgp_outputs = re.match(
|
||||||
r"{(.*)}(.*)\(([^()]+)\)", code).groups()
|
r"{(.*)}(.*)\(([^()]+)\)", code).groups()
|
||||||
|
|
||||||
@ -43,25 +40,21 @@ class UnsignedCGPCircuit(GeneralCircuit):
|
|||||||
int, cgp_prefix.split(","))
|
int, cgp_prefix.split(","))
|
||||||
|
|
||||||
assert sum(
|
assert sum(
|
||||||
input_widths) == c_in, f"CGP input widht {c_in} doesn't match input_widhts {input_widths}"
|
input_widths) == c_in, f"CGP input width {c_in} doesn't match input_widths {input_widths}"
|
||||||
#assert c_rows == 1, f"Only one-row CGP is supported {c_rows}x{c_cols}"
|
|
||||||
|
|
||||||
inputs = [Bus(N=bw, prefix=f"input_{chr(i)}")
|
inputs = [Bus(N=bw, prefix=f"input_{chr(i)}")
|
||||||
for i, bw in enumerate(input_widths, start=0x61)]
|
for i, bw in enumerate(input_widths, start=0x61)]
|
||||||
#vals = Bus(N=c_rows * c_cols, prefix=f"{prefix}_data")
|
|
||||||
|
|
||||||
# adding values to the list
|
# Adding values to the list
|
||||||
self.vals = {}
|
self.vals = {}
|
||||||
j = 2 # start from two, 0=false, 1 = true
|
j = 2 # Start from two, 0=False, 1=True
|
||||||
for iid, bw in enumerate(input_widths):
|
for iid, bw in enumerate(input_widths):
|
||||||
for i in range(bw):
|
for i in range(bw):
|
||||||
assert j not in self.vals
|
assert j not in self.vals
|
||||||
self.vals[j] = inputs[iid].get_wire(i)
|
self.vals[j] = inputs[iid].get_wire(i)
|
||||||
j += 1
|
j += 1
|
||||||
|
|
||||||
|
|
||||||
super().__init__(prefix=prefix, name=name, out_N=c_out, inputs=inputs, **kwargs)
|
super().__init__(prefix=prefix, name=name, out_N=c_out, inputs=inputs, **kwargs)
|
||||||
|
|
||||||
cgp_core = cgp_core.split(")(")
|
cgp_core = cgp_core.split(")(")
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
@ -74,38 +67,37 @@ class UnsignedCGPCircuit(GeneralCircuit):
|
|||||||
comp_set = dict(prefix=f"{self.prefix}_core_{i:03d}", parent_component=self)
|
comp_set = dict(prefix=f"{self.prefix}_core_{i:03d}", parent_component=self)
|
||||||
|
|
||||||
a, b = self._get_wire(in_a), self._get_wire(in_b)
|
a, b = self._get_wire(in_a), self._get_wire(in_b)
|
||||||
if fn == 0: # identity
|
if fn == 0: # IDENTITY
|
||||||
o = a
|
o = a
|
||||||
elif fn == 1: # not
|
elif fn == 1: # NOT
|
||||||
o = self.add_component(NotGate(a, **comp_set)).out
|
o = self.add_component(NotGate(a, **comp_set)).out
|
||||||
elif fn == 2: # and
|
elif fn == 2: # AND
|
||||||
o = self.add_component(AndGate(a, b, **comp_set)).out
|
o = self.add_component(AndGate(a, b, **comp_set)).out
|
||||||
elif fn == 3: # or
|
elif fn == 3: # OR
|
||||||
o = self.add_component(OrGate(a, b, **comp_set)).out
|
o = self.add_component(OrGate(a, b, **comp_set)).out
|
||||||
elif fn == 4: # xor
|
elif fn == 4: # XOR
|
||||||
o = self.add_component(XorGate(a, b, **comp_set)).out
|
o = self.add_component(XorGate(a, b, **comp_set)).out
|
||||||
elif fn == 5: # nand
|
elif fn == 5: # NAND
|
||||||
o = self.add_component(NandGate(a, b, **comp_set)).out
|
o = self.add_component(NandGate(a, b, **comp_set)).out
|
||||||
elif fn == 6: # nor
|
elif fn == 6: # NOR
|
||||||
o = self.add_component(NorGate(a, b, **comp_set)).out
|
o = self.add_component(NorGate(a, b, **comp_set)).out
|
||||||
elif fn == 7: # xnor
|
elif fn == 7: # XNOR
|
||||||
o = self.add_component(XnorGate(a, b, **comp_set)).out
|
o = self.add_component(XnorGate(a, b, **comp_set)).out
|
||||||
elif fn == 8: # true
|
elif fn == 8: # TRUE
|
||||||
o = ConstantWireValue1()
|
o = ConstantWireValue1()
|
||||||
elif fn == 9: # false
|
elif fn == 9: # FALSE
|
||||||
o = ConstantWireValue0()
|
o = ConstantWireValue0()
|
||||||
|
|
||||||
assert i not in self.vals
|
assert i not in self.vals
|
||||||
self.vals[i] = o
|
self.vals[i] = o
|
||||||
|
|
||||||
# output connection
|
# Output connection
|
||||||
for i, o in enumerate(map(int, cgp_outputs.split(","))):
|
for i, o in enumerate(map(int, cgp_outputs.split(","))):
|
||||||
w = self._get_wire(o)
|
w = self._get_wire(o)
|
||||||
#print(i, o, w, w.name)
|
|
||||||
self.out.connect(i, w)
|
self.out.connect(i, w)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_inputs_outputs(code : str):
|
def get_inputs_outputs(code: str):
|
||||||
cgp_prefix, cgp_core, cgp_outputs = re.match(
|
cgp_prefix, cgp_core, cgp_outputs = re.match(
|
||||||
r"{(.*)}(.*)\(([^()]+)\)", code).groups()
|
r"{(.*)}(.*)\(([^()]+)\)", code).groups()
|
||||||
|
|
||||||
@ -114,7 +106,6 @@ class UnsignedCGPCircuit(GeneralCircuit):
|
|||||||
|
|
||||||
return c_in, c_out
|
return c_in, c_out
|
||||||
|
|
||||||
|
|
||||||
def _get_wire(self, i):
|
def _get_wire(self, i):
|
||||||
if i == 0:
|
if i == 0:
|
||||||
return ConstantWireValue0()
|
return ConstantWireValue0()
|
||||||
@ -122,11 +113,9 @@ class UnsignedCGPCircuit(GeneralCircuit):
|
|||||||
return ConstantWireValue1()
|
return ConstantWireValue1()
|
||||||
return self.vals[i]
|
return self.vals[i]
|
||||||
|
|
||||||
#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)
|
|
||||||
|
|
||||||
class SignedCGPCircuit(UnsignedCGPCircuit):
|
class SignedCGPCircuit(UnsignedCGPCircuit):
|
||||||
|
"""Signed circuit variant that loads CGP code and is able to export it to C/verilog/Blif/CGP."""
|
||||||
def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs):
|
def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs):
|
||||||
super().__init__(code=code, input_widths=input_widths, prefix=prefix, name=name, signed=True, **kwargs)
|
super().__init__(code=code, input_widths=input_widths, prefix=prefix, name=name, signed=True, **kwargs)
|
||||||
self.c_data_type = "int64_t"
|
self.c_data_type = "int64_t"
|
@ -12,3 +12,8 @@ from ariths_gen.multi_bit_circuits.multipliers.dadda_multiplier import (
|
|||||||
UnsignedDaddaMultiplier,
|
UnsignedDaddaMultiplier,
|
||||||
SignedDaddaMultiplier
|
SignedDaddaMultiplier
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from ariths_gen.multi_bit_circuits.multipliers.wallace_csa_multiplier import (
|
||||||
|
UnsignedWallaceCSAMultiplier,
|
||||||
|
SignedWallaceCSAMultiplier
|
||||||
|
)
|
||||||
|
@ -165,7 +165,7 @@ class SignedDaddaMultiplier(MultiplierCircuit):
|
|||||||
# Get starting stage and maximum possible column height
|
# Get starting stage and maximum possible column height
|
||||||
self.stage, self.d = self.get_maximum_height(initial_value=min(self.a.N, self.b.N))
|
self.stage, self.d = self.get_maximum_height(initial_value=min(self.a.N, self.b.N))
|
||||||
# Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication
|
# Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication
|
||||||
self.columns = self.init_column_heights(signed=True)
|
self.columns = self.init_column_heights()
|
||||||
|
|
||||||
# Not used for 1 bit multiplier
|
# Not used for 1 bit multiplier
|
||||||
if self.N != 1:
|
if self.N != 1:
|
||||||
|
@ -0,0 +1,214 @@
|
|||||||
|
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.adders import (
|
||||||
|
CarrySaveAdderComponent,
|
||||||
|
UnsignedCarryLookaheadAdder
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UnsignedWallaceCSAMultiplier(MultiplierCircuit):
|
||||||
|
"""Class representing unsigned wallace multiplier composed of carry save adder components.
|
||||||
|
|
||||||
|
Unsigned wallace multiplier represents fast N-bit multiplier which utilizes
|
||||||
|
the functionality of wallace tree reduction algorithm proposed by Chris Wallace.
|
||||||
|
|
||||||
|
First partial products are calculated for each bit pair that form the partial product multiplication rows.
|
||||||
|
This implementation uses carry save adder components to efficiently implement reduction of partial products utilizing the parallelism of the carry save adders.
|
||||||
|
At last the reduced pairs are inserted into chosen multi bit unsigned adder to execute their summation and obtain the final output bits.
|
||||||
|
|
||||||
|
Wallace tree algorithm is described more in detail here:
|
||||||
|
https://en.wikipedia.org/wiki/Wallace_tree
|
||||||
|
|
||||||
|
It presents a faster version of multiplier opposed to the conventional architectures that are composed of interconnected half/full adders.
|
||||||
|
```
|
||||||
|
PP7 PP6 PP5 PP4 PP3 PP2 PP1 PP0
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
│ │ ┌▼──▼──▼┐ ┌▼──▼──▼┐
|
||||||
|
│ │ │ CSA │ │ CSA │
|
||||||
|
│ │ └─┬───┬─┘ └─┬───┬─┘
|
||||||
|
│ │ │c1 │s1 │c0 │s0
|
||||||
|
└┐ │ ┌──┘ └────┐ │ ┌┘
|
||||||
|
┌▼──▼──▼┐ ┌▼──▼──▼┐
|
||||||
|
│ CSA │ │ CSA │
|
||||||
|
└─┬───┬─┘ └─┬───┬─┘
|
||||||
|
│c4 │s4 ┌───────┘c3 │s3
|
||||||
|
│ └──┐ │ ┌────────┘
|
||||||
|
│ ┌▼──▼──▼┐
|
||||||
|
│ │ CSA │
|
||||||
|
│ └─┬───┬─┘
|
||||||
|
│ ┌────┘c5 │s5
|
||||||
|
│ │ ┌─────┘
|
||||||
|
┌▼──▼──▼┐
|
||||||
|
│ CSA │
|
||||||
|
└─┬───┬─┘
|
||||||
|
│c6 │s6
|
||||||
|
┌─▼───▼─┐
|
||||||
|
│ CPA │
|
||||||
|
└───┬───┘
|
||||||
|
o
|
||||||
|
```
|
||||||
|
|
||||||
|
Description of the __init__ method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
a (Bus): First input bus.
|
||||||
|
b (Bus): Second input bus.
|
||||||
|
prefix (str, optional): Prefix name of unsigned csa wallace multiplier. Defaults to "".
|
||||||
|
name (str, optional): Name of unsigned csa wallace multiplier. Defaults to "u_wallaceCSA_cla".
|
||||||
|
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder.
|
||||||
|
"""
|
||||||
|
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_wallaceCSA_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||||
|
self.N = max(a.N, b.N)
|
||||||
|
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# Initialize all rows partial products forming AND gates matrix
|
||||||
|
self.rows = self.init_row_lengths()
|
||||||
|
|
||||||
|
# Zero extension of partial product rows
|
||||||
|
for i in range(0, len(self.rows)):
|
||||||
|
self.rows[i] = Bus(prefix=self.rows[i].prefix, wires_list=[ConstantWireValue0() for _ in range(0, i)] + self.rows[i].bus)
|
||||||
|
|
||||||
|
while len(self.rows) > 2:
|
||||||
|
# Gradual creation of unsigned csa adder components to reduce the pp rows to the total count of 2
|
||||||
|
pp_index = 0
|
||||||
|
while pp_index < len(self.rows) and (pp_index+2) < len(self.rows):
|
||||||
|
csa_reduction = CarrySaveAdderComponent(a=self.rows[pp_index], b=self.rows[pp_index+1], c=self.rows[pp_index+2], prefix=self.prefix+"_csa"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), inner_component=True)
|
||||||
|
self.add_component(csa_reduction)
|
||||||
|
|
||||||
|
# 3 pp rows have been reduced to 2
|
||||||
|
[self.rows.pop(pp_index) for i in range(3)]
|
||||||
|
|
||||||
|
# Append rows of sum and carry results from csa calculation
|
||||||
|
csa_sums_N = self.out.N if csa_reduction.sum_bits.N > self.out.N-1 else csa_reduction.sum_bits.N
|
||||||
|
csa_sums = Bus(prefix=self.prefix+"_csa_s"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), N=csa_sums_N)
|
||||||
|
csa_sums.connect_bus(connecting_bus=csa_reduction.out, end_connection_pos=csa_sums_N)
|
||||||
|
|
||||||
|
csa_carries_N = self.out.N if csa_reduction.carry_bits.N > self.out.N-1 else csa_reduction.carry_bits.N
|
||||||
|
csa_carries = Bus(prefix=self.prefix+"_csa_c"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), N=csa_carries_N)
|
||||||
|
csa_carries.connect_bus(connecting_bus=csa_reduction.out, start_connection_pos=int(csa_reduction.out.N/2), end_connection_pos=int(csa_reduction.out.N/2)+csa_carries.N, offset=int(csa_reduction.out.N/2))
|
||||||
|
|
||||||
|
self.rows.insert(pp_index, csa_carries)
|
||||||
|
self.rows.insert(pp_index, csa_sums)
|
||||||
|
|
||||||
|
# Update of the number of pp rows
|
||||||
|
pp_index += 2
|
||||||
|
|
||||||
|
# Final addition of remaining bits using chosen unsigned multi bit adder
|
||||||
|
# Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
|
||||||
|
adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(self.rows[0].N)
|
||||||
|
adder_a = Bus(prefix="a", N=self.rows[0].N)
|
||||||
|
adder_b = Bus(prefix="b", N=self.rows[1].N)
|
||||||
|
[adder_a.connect(w, self.rows[0].get_wire(w)) for w in range(0, self.rows[0].N)]
|
||||||
|
[adder_b.connect(w, self.rows[1].get_wire(w)) for w in range(0, self.rows[1].N)]
|
||||||
|
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True)
|
||||||
|
self.add_component(final_adder)
|
||||||
|
[self.out.connect(o, final_adder.out.get_wire(o), inserted_wire_desired_index=o) for o in range(0, final_adder.out.N-1)]
|
||||||
|
|
||||||
|
|
||||||
|
class SignedWallaceCSAMultiplier(MultiplierCircuit):
|
||||||
|
"""Class representing signed wallace multiplier composed of carry save adder components.
|
||||||
|
|
||||||
|
Signed wallace multiplier represents fast N-bit multiplier which utilizes
|
||||||
|
the functionality of wallace tree reduction algorithm proposed by Chris Wallace and uses Baugh-Wooley algorithm
|
||||||
|
to perform signed multiplication.
|
||||||
|
|
||||||
|
First partial products are calculated for each bit pair that form the partial product multiplication rows.
|
||||||
|
This implementation uses carry save adder components to efficiently implement reduction of partial products utilizing the parallelism of the carry save adders.
|
||||||
|
At last the reduced pairs are inserted into chosen multi bit unsigned adder to execute their summation and obtain the final output bits, additional XOR gate serve the necessary sign extension.
|
||||||
|
|
||||||
|
Wallace tree algorithm is described more in detail here:
|
||||||
|
https://en.wikipedia.org/wiki/Wallace_tree
|
||||||
|
|
||||||
|
It presents a faster version of multiplier opposed to the conventional architectures that are composed of interconnected half/full adders.
|
||||||
|
|
||||||
|
Description of the __init__ method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
a (Bus): First input bus.
|
||||||
|
b (Bus): Second input bus.
|
||||||
|
prefix (str, optional): Prefix name of signed csa wallace multiplier. Defaults to "".
|
||||||
|
name (str, optional): Name of signed csa wallace multiplier. Defaults to "s_wallaceCSA_cla".
|
||||||
|
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder.
|
||||||
|
"""
|
||||||
|
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_wallaceCSA_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||||
|
self.N = max(a.N, b.N)
|
||||||
|
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Initialize all rows partial products forming AND gates matrix
|
||||||
|
self.rows = self.init_row_lengths()
|
||||||
|
|
||||||
|
# Zero extension of partial product rows
|
||||||
|
for i in range(0, len(self.rows)):
|
||||||
|
self.rows[i] = Bus(prefix=self.rows[i].prefix, wires_list=[ConstantWireValue0() for _ in range(0, i)] + self.rows[i].bus)
|
||||||
|
|
||||||
|
while len(self.rows) > 2:
|
||||||
|
# Gradual creation of signed csa adder components to reduce the pp rows to the total count of 2
|
||||||
|
pp_index = 0
|
||||||
|
while pp_index < len(self.rows) and (pp_index+2) < len(self.rows):
|
||||||
|
csa_reduction = CarrySaveAdderComponent(a=self.rows[pp_index], b=self.rows[pp_index+1], c=self.rows[pp_index+2], prefix=self.prefix+"_csa"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), inner_component=True, signed=True)
|
||||||
|
self.add_component(csa_reduction)
|
||||||
|
|
||||||
|
# 3 pp rows have been reduced to 2
|
||||||
|
[self.rows.pop(pp_index) for i in range(3)]
|
||||||
|
|
||||||
|
# Append rows of sum and carry results from csa calculation
|
||||||
|
csa_sums_N = self.out.N if csa_reduction.sum_bits.N > self.out.N-1 else csa_reduction.sum_bits.N
|
||||||
|
csa_sums = Bus(prefix=self.prefix+"_csa_s"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), N=csa_sums_N)
|
||||||
|
csa_sums.connect_bus(connecting_bus=csa_reduction.out, end_connection_pos=csa_sums_N)
|
||||||
|
|
||||||
|
csa_carries_N = self.out.N if csa_reduction.carry_bits.N > self.out.N-1 else csa_reduction.carry_bits.N
|
||||||
|
csa_carries = Bus(prefix=self.prefix+"_csa_c"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), N=csa_carries_N)
|
||||||
|
csa_carries.connect_bus(connecting_bus=csa_reduction.out, start_connection_pos=int(csa_reduction.out.N/2), end_connection_pos=int(csa_reduction.out.N/2)+csa_carries.N, offset=int(csa_reduction.out.N/2))
|
||||||
|
|
||||||
|
self.rows.insert(pp_index, csa_carries)
|
||||||
|
self.rows.insert(pp_index, csa_sums)
|
||||||
|
|
||||||
|
# Update of the number of pp rows
|
||||||
|
pp_index += 2
|
||||||
|
|
||||||
|
# Final addition of remaining bits using chosen unsigned multi bit adder
|
||||||
|
# Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
|
||||||
|
adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(self.rows[0].N)
|
||||||
|
adder_a = Bus(prefix="a", N=self.rows[0].N)
|
||||||
|
adder_b = Bus(prefix="b", N=self.rows[1].N)
|
||||||
|
[adder_a.connect(w, self.rows[0].get_wire(w)) for w in range(0, self.rows[0].N)]
|
||||||
|
[adder_b.connect(w, self.rows[1].get_wire(w)) for w in range(0, self.rows[1].N)]
|
||||||
|
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True)
|
||||||
|
self.add_component(final_adder)
|
||||||
|
[self.out.connect(o, final_adder.out.get_wire(o), inserted_wire_desired_index=o) for o in range(0, final_adder.out.N-1)]
|
||||||
|
|
||||||
|
# Final XOR to ensure proper sign extension
|
||||||
|
obj_xor = XorGate(ConstantWireValue1(), self.out.get_wire(self.out.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
|
||||||
|
self.add_component(obj_xor)
|
||||||
|
self.out.connect(self.out.N-1, obj_xor.out)
|
@ -156,7 +156,7 @@ class SignedWallaceMultiplier(MultiplierCircuit):
|
|||||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||||
|
|
||||||
# Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication
|
# Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication
|
||||||
self.columns = self.init_column_heights(signed=True)
|
self.columns = self.init_column_heights()
|
||||||
|
|
||||||
# Not used for 1 bit multiplier
|
# Not used for 1 bit multiplier
|
||||||
if self.N != 1:
|
if self.N != 1:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from .wires import Wire
|
from .wires import Wire, ConstantWireValue0, ConstantWireValue1
|
||||||
|
|
||||||
|
|
||||||
class Bus():
|
class Bus():
|
||||||
@ -48,23 +48,23 @@ class Bus():
|
|||||||
"""
|
"""
|
||||||
return self.out_bus
|
return self.out_bus
|
||||||
|
|
||||||
def bus_extend(self, N: int, prefix: str = "bus", last_wire_extend: bool = True):
|
def bus_extend(self, N: int, prefix: str = "bus", desired_extension_wire: Wire = ConstantWireValue0()):
|
||||||
"""Provides bus extension to contain more wires.
|
"""Provides bus extension to contain more wires.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
N (int): Number of wires in the bus. Defaults to 1.
|
N (int): Number of wires in the bus. Defaults to 1.
|
||||||
prefix (str, optional): Prefix name of the bus. Defaults to "bus".
|
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.
|
desired_extension_wire (Wire, optional): Specifies the wire that should be connected to all of the extending bus wires. Defaults to ConstantWireValue0().
|
||||||
"""
|
"""
|
||||||
# Checks if any extension is neccesarry and if so, proceeds to wire extend the bus
|
# Checks if any extension is neccesarry and if so, proceeds to wire extend the bus
|
||||||
if self.N < N:
|
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)
|
# 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)]
|
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
|
|
||||||
|
|
||||||
|
for w_index in range(self.N, N):
|
||||||
|
self.connect(bus_wire_index=w_index, inner_component_out_wire=desired_extension_wire)
|
||||||
|
|
||||||
|
self.N = N
|
||||||
|
|
||||||
def get_wire(self, wire_index: int = 0):
|
def get_wire(self, wire_index: int = 0):
|
||||||
"""Retrieves a wire from the bus by a given index.
|
"""Retrieves a wire from the bus by a given index.
|
||||||
@ -100,7 +100,7 @@ class Bus():
|
|||||||
elif inserted_wire_desired_index != -1:
|
elif inserted_wire_desired_index != -1:
|
||||||
self.bus[bus_wire_index] = Wire(name=inner_component_out_wire.name, prefix=inner_component_out_wire.parent_bus.prefix, index=inserted_wire_index, value=inner_component_out_wire.value, parent_bus=self)
|
self.bus[bus_wire_index] = Wire(name=inner_component_out_wire.name, prefix=inner_component_out_wire.parent_bus.prefix, index=inserted_wire_index, value=inner_component_out_wire.value, parent_bus=self)
|
||||||
|
|
||||||
def connect_bus(self, connecting_bus: object, start_connection_pos: int = 0, end_connection_pos: int = -1):
|
def connect_bus(self, connecting_bus: object, start_connection_pos: int = 0, end_connection_pos: int = -1, offset: int = 0):
|
||||||
"""Ensures connection of specified bus wires to this bus wires.
|
"""Ensures connection of specified bus wires to this bus wires.
|
||||||
|
|
||||||
Used for connection of some inner circuit component's output bus (`connecting_bus`) wires
|
Used for connection of some inner circuit component's output bus (`connecting_bus`) wires
|
||||||
@ -110,11 +110,12 @@ class Bus():
|
|||||||
connecting_bus (object): Specifies the connecting bus.
|
connecting_bus (object): Specifies the connecting bus.
|
||||||
start_connection_pos (int, optional): Specifies the position from which to start interconnecting wires from the `connecting_bus` to this `self` bus. Defaults to 0.
|
start_connection_pos (int, optional): Specifies the position from which to start interconnecting wires from the `connecting_bus` to this `self` bus. Defaults to 0.
|
||||||
end_connection_pos (int, optional): Specifies the position from which to end interconnecting wires from the `connecting_bus` to this `self` bus. Defaults to -1.
|
end_connection_pos (int, optional): Specifies the position from which to end interconnecting wires from the `connecting_bus` to this `self` bus. Defaults to -1.
|
||||||
|
offset (int, optional): Specifies the offset wire index position in the `self` bus for proper connection (i.e. wire at index position 5 in the `connecting_bus` with offset set to 5 will be connected to `self` bus index position 0). Default to 0.
|
||||||
"""
|
"""
|
||||||
if end_connection_pos == -1:
|
if end_connection_pos == -1:
|
||||||
end_connection_pos = self.N
|
end_connection_pos = self.N
|
||||||
|
|
||||||
[self.connect(o, connecting_bus.get_wire(o), inserted_wire_desired_index=o) for o in range(start_connection_pos, end_connection_pos)]
|
[self.connect(o-offset, connecting_bus.get_wire(o), inserted_wire_desired_index=o) for o in range(start_connection_pos, end_connection_pos)]
|
||||||
|
|
||||||
""" PYTHON CODE GENERATION """
|
""" PYTHON CODE GENERATION """
|
||||||
def return_bus_wires_values_python_flat(self):
|
def return_bus_wires_values_python_flat(self):
|
||||||
@ -123,7 +124,10 @@ class Bus():
|
|||||||
Returns:
|
Returns:
|
||||||
str: Python code for assigning wire values into bus represented in Python code variable.
|
str: Python code for assigning wire values into bus represented in Python code variable.
|
||||||
"""
|
"""
|
||||||
return "".join([f" {self.prefix} = 0\n"] + [f" {self.prefix} |= {w.return_wire_value_python_flat(offset=self.bus.index(w))}" for w in self.bus])
|
# Ensures correct binding between the bus wire index and the wire itself
|
||||||
|
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||||
|
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||||
|
return "".join([f" {self.prefix} = 0\n"] + [f" {self.prefix} |= {w[1].return_wire_value_python_flat(offset=w[0])}" for w in mapped_positions])
|
||||||
|
|
||||||
def return_bus_wires_sign_extend_python(self):
|
def return_bus_wires_sign_extend_python(self):
|
||||||
"""Sign extends the bus's corresponding Python variable (object) to ensure proper Python code variable signedness.
|
"""Sign extends the bus's corresponding Python variable (object) to ensure proper Python code variable signedness.
|
||||||
@ -146,14 +150,16 @@ class Bus():
|
|||||||
"""
|
"""
|
||||||
return f" {self.c_type} {self.prefix} = 0;\n"
|
return f" {self.c_type} {self.prefix} = 0;\n"
|
||||||
|
|
||||||
|
|
||||||
def return_bus_wires_values_c_flat(self):
|
def return_bus_wires_values_c_flat(self):
|
||||||
"""Retrieves values from bus's wires and stores them in bus's corresponding C variable at proper offset bit position in the bus for flat generation.
|
"""Retrieves values from bus's wires and stores them in bus's corresponding C variable at proper offset bit position in the bus for flat generation.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: C code for assigning wire values into bus represented in C code variable.
|
str: C code for assigning wire values into bus represented in C code variable.
|
||||||
"""
|
"""
|
||||||
return "".join([f" {self.prefix} |= {w.return_wire_value_c_flat(offset=self.bus.index(w))}" for w in self.bus])
|
# Ensures correct binding between the bus wire index and the wire itself
|
||||||
|
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||||
|
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||||
|
return "".join([f" {self.prefix} |= {w[1].return_wire_value_c_flat(offset=w[0])}" for w in mapped_positions])
|
||||||
|
|
||||||
def return_bus_wires_values_c_hier(self):
|
def return_bus_wires_values_c_hier(self):
|
||||||
"""Retrieves values from bus's wires and stores them in bus's corresponding C variable at proper offset bit position in the bus for hierarchical generation.
|
"""Retrieves values from bus's wires and stores them in bus's corresponding C variable at proper offset bit position in the bus for hierarchical generation.
|
||||||
@ -161,7 +167,10 @@ class Bus():
|
|||||||
Returns:
|
Returns:
|
||||||
str: C code for assigning wire values into bus represented in C code variable.
|
str: C code for assigning wire values into bus represented in C code variable.
|
||||||
"""
|
"""
|
||||||
return "".join([f" {self.prefix} |= {w.return_wire_value_c_hier(offset=self.bus.index(w))}" for w in self.bus])
|
# Ensures correct binding between the bus wire index and the wire itself
|
||||||
|
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||||
|
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||||
|
return "".join([f" {self.prefix} |= {w[1].return_wire_value_c_hier(offset=w[0])}" for w in mapped_positions])
|
||||||
|
|
||||||
def return_bus_wires_sign_extend_c(self):
|
def return_bus_wires_sign_extend_c(self):
|
||||||
"""Sign extends the bus's corresponding C variable to ensure proper C code variable signedness.
|
"""Sign extends the bus's corresponding C variable to ensure proper C code variable signedness.
|
||||||
@ -182,7 +191,10 @@ class Bus():
|
|||||||
Returns:
|
Returns:
|
||||||
str: Verilog code for assigning wire values into bus represented in Verilog code bus variable.
|
str: Verilog code for assigning wire values into bus represented in Verilog code bus variable.
|
||||||
"""
|
"""
|
||||||
return "".join([f" assign {self.prefix}[{self.bus.index(w)}] = {w.return_wire_value_v_flat()}" for w in self.bus])
|
# Ensures correct binding between the bus wire index and the wire itself
|
||||||
|
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||||
|
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||||
|
return "".join([f" assign {self.prefix}[{w[0]}] = {w[1].return_wire_value_v_flat()}" for w in mapped_positions])
|
||||||
|
|
||||||
def return_bus_wires_values_v_hier(self):
|
def return_bus_wires_values_v_hier(self):
|
||||||
"""Retrieves values from bus's wires and stores them in bus's corresponding Verilog variable at proper offset bit position in the bus for hierarchical generation.
|
"""Retrieves values from bus's wires and stores them in bus's corresponding Verilog variable at proper offset bit position in the bus for hierarchical generation.
|
||||||
@ -190,7 +202,10 @@ class Bus():
|
|||||||
Returns:
|
Returns:
|
||||||
str: Verilog code for assigning wire values into bus represented in Verilog code variable.
|
str: Verilog code for assigning wire values into bus represented in Verilog code variable.
|
||||||
"""
|
"""
|
||||||
return "".join([f" assign {self.prefix}[{self.bus.index(w)}] = {w.return_wire_value_v_hier()}" for w in self.bus])
|
# Ensures correct binding between the bus wire index and the wire itself
|
||||||
|
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||||
|
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||||
|
return "".join([f" assign {self.prefix}[{w[0]}] = {w[1].return_wire_value_v_hier()}" for w in mapped_positions])
|
||||||
|
|
||||||
def get_unique_assign_out_wires_v(self, circuit_block: object):
|
def get_unique_assign_out_wires_v(self, circuit_block: object):
|
||||||
"""Returns bus's wires used for hierarchical one bit subcomponent's function block invocation and output wires assignments.
|
"""Returns bus's wires used for hierarchical one bit subcomponent's function block invocation and output wires assignments.
|
||||||
@ -215,7 +230,10 @@ class Bus():
|
|||||||
Returns:
|
Returns:
|
||||||
str: Blif code for declaration of individual bus wires.
|
str: Blif code for declaration of individual bus wires.
|
||||||
"""
|
"""
|
||||||
return "".join([f" {w.get_declaration_blif(prefix=self.prefix, offset=self.bus.index(w), array=array)}" for w in self.bus])
|
# Ensures correct binding between the bus wire index and the wire itself
|
||||||
|
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||||
|
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||||
|
return "".join([f" {w[1].get_declaration_blif(prefix=self.prefix, offset=w[0], array=array)}" for w in mapped_positions])
|
||||||
|
|
||||||
def get_wire_assign_blif(self, output: bool = False):
|
def get_wire_assign_blif(self, output: bool = False):
|
||||||
"""Assign all bits from the bus as each individual wires or assign wires into the corresponding output bus position in Blif code representation.
|
"""Assign all bits from the bus as each individual wires or assign wires into the corresponding output bus position in Blif code representation.
|
||||||
@ -226,7 +244,10 @@ class Bus():
|
|||||||
Returns:
|
Returns:
|
||||||
str: Blif code for bus wires assignments.
|
str: Blif code for bus wires assignments.
|
||||||
"""
|
"""
|
||||||
return "".join([w.get_assign_blif(prefix=self.prefix+f"[{self.bus.index(w)}]", output=output) for w in self.bus])
|
# Ensures correct binding between the bus wire index and the wire itself
|
||||||
|
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||||
|
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||||
|
return "".join([w[1].get_assign_blif(prefix=self.prefix+f"[{w[0]}]", output=output) for w in mapped_positions])
|
||||||
|
|
||||||
def get_unique_assign_out_wires_blif(self, function_block_out_bus: object):
|
def get_unique_assign_out_wires_blif(self, function_block_out_bus: object):
|
||||||
"""Assigns unique output wires to their respective outputs of subcomponent's function block modul in hierarchical Blif subcomponent's invocation.
|
"""Assigns unique output wires to their respective outputs of subcomponent's function block modul in hierarchical Blif subcomponent's invocation.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user