Optimized circuits generation, refactored code, fixed cla, added new csa, array divider circuits and create yosys equivalence check script. TBD: Documentation and sample generated circuits.

This commit is contained in:
honzastor 2021-04-21 11:33:07 +02:00
parent 068def0226
commit 8e950fc51f
32 changed files with 3072 additions and 1766 deletions

View File

@ -1,5 +1,7 @@
from ariths_gen.wire_components import(
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
@ -15,7 +17,10 @@ from ariths_gen.one_bit_circuits.logic_gates import(
from ariths_gen.one_bit_circuits.one_bit_components import(
HalfAdder,
FullAdder
FullAdder,
PGLogicBlock,
FullAdderPG,
TwoOneMultiplexer
)
from ariths_gen.multi_bit_circuits.adders import(
@ -24,7 +29,9 @@ from ariths_gen.multi_bit_circuits.adders import(
UnsignedRippleCarryAdder,
SignedCarryLookaheadAdder,
SignedPGRippleCarryAdder,
SignedRippleCarryAdder
SignedRippleCarryAdder,
UnsignedCarrySkipAdder,
SignedCarrySkipAdder,
)
from ariths_gen.multi_bit_circuits.multipliers import(
@ -35,6 +42,10 @@ from ariths_gen.multi_bit_circuits.multipliers import(
SignedDaddaMultiplier,
SignedWallaceMultiplier
)
from ariths_gen.multi_bit_circuits.dividers import(
ArrayDivider
)
import sys
import os
@ -67,7 +78,16 @@ if __name__ == "__main__":
circuit = SignedPGRippleCarryAdder(a, b, prefix=name)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
#CLA with PG
#CSA with 4 bit CSA blocks (default)
name = f"{representation}_u_csa{N}"
circuit = UnsignedCarrySkipAdder(a, b, prefix=name)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_s_csa{N}"
circuit = SignedCarrySkipAdder(a, b, prefix=name)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
#CLA with 4 bit CLA blocks (default)
name = f"{representation}_u_cla{N}"
circuit = UnsignedCarryLookaheadAdder(a, b, prefix=name)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
@ -87,6 +107,14 @@ if __name__ == "__main__":
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
# Wallace
name = f"{representation}_u_wallace_cla{N}"
circuit = UnsignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_s_wallace_cla{N}"
circuit = SignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_u_wallace_rca{N}"
circuit = UnsignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedRippleCarryAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
@ -102,8 +130,24 @@ if __name__ == "__main__":
name = f"{representation}_s_wallace_pg_rca{N}"
circuit = SignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_u_wallace_csa{N}"
circuit = UnsignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarrySkipAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_s_wallace_csa{N}"
circuit = SignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarrySkipAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
# Dadda
name = f"{representation}_u_dadda_cla{N}"
circuit = UnsignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_s_dadda_cla{N}"
circuit = SignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_u_dadda_rca{N}"
circuit = UnsignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedRippleCarryAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
@ -112,7 +156,6 @@ if __name__ == "__main__":
circuit = SignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedRippleCarryAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_u_dadda_pg_rca{N}"
circuit = UnsignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
@ -120,3 +163,17 @@ if __name__ == "__main__":
name = f"{representation}_s_dadda_pg_rca{N}"
circuit = SignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_u_dadda_csa{N}"
circuit = UnsignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarrySkipAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
name = f"{representation}_s_dadda_csa{N}"
circuit = SignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarrySkipAdder)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))
# Arrdiv
name = f"{representation}_arrdiv{N}"
circuit = ArrayDivider(a, b, prefix=name)
circuit.get_v_code_hier(open(f"{directory}/{name}.v", "w"))

View File

@ -10,5 +10,6 @@ from ariths_gen.one_bit_circuits import(
from ariths_gen.multi_bit_circuits import(
adders,
multipliers
multipliers,
dividers
)

View File

@ -1,15 +0,0 @@
from .arithmetic_circuit import (
ArithmeticCircuit
)
from .multiplier_circuit import (
MultiplierCircuit
)
from .two_input_one_bit_circuit import (
TwoInputOneBitCircuit
)
from .three_input_one_bit_circuit import (
ThreeInputOneBitCircuit
)

View File

@ -0,0 +1,7 @@
from .arithmetic_circuit import (
ArithmeticCircuit
)
from .multiplier_circuit import (
MultiplierCircuit
)

View File

@ -1,33 +1,28 @@
from ariths_gen.one_bit_circuits.logic_gates import (
LogicGate,
AndGate,
NandGate,
OrGate,
NorGate,
XorGate,
XnorGate,
NotGate
)
from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import OneInputLogicGate, TwoInputLogicGate
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
class ArithmeticCircuit():
"""Class represents a general arithmetic circuit and ensures their 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):
self.components = []
self.circuit_wires = []
self.inputs = []
self.circuit_gates = []
self.c_data_type = "uint64_t"
self.N = 1
def add_component(self, component):
"""Adds component into a list of circuit's inner subcomponents.
"""Adds a component into list of circuit's inner subcomponents.
Args:
component: Subcomponent to be added into list of components composing described circuit.
@ -45,28 +40,46 @@ class ArithmeticCircuit():
"""
return self.components[-number]
def get_instance_num(self, cls):
def get_instance_num(self, cls, count_disabled_gates: bool = True):
"""Informs how many instances of the same type are already present inside circuit's components list.
Args:
cls (type): Class type of which to search for instances.
cls (type): Class type for which to count the number of instances in the components list.
count_disabled_gates (bool, optional): Indicates whether logic gates that aren't generated should be also counted. Defaults to True.
Returns:
int: Number of instances of the same class type.
"""
return sum(isinstance(c, cls) for c in self.components)
if issubclass(cls, TwoInputLogicGate) and count_disabled_gates is False:
return sum(isinstance(c, cls) for c in self.components if isinstance(c, cls) and c.disable_generation is False)
else:
return sum(isinstance(c, cls) for c in self.components)
def get_circuit_gates(self):
"""Gets a list of all the logic gates in circuit that should be generated.
Returns:
list: List of composite logic gates.
"""
gates = []
for c in self.components:
if isinstance(c, TwoInputLogicGate):
if c.disable_generation is False:
gates.append(c)
else:
gates.extend((c.get_circuit_gates()))
return gates
def get_one_bit_components(self):
"""Retrieves list of all one bit circuits (besides logic gates) present as subcomponents inside the circuit.
"""Retrieves a list of all the one bit circuits (besides logic gates) present as subcomponents inside the circuit.
Returns:
list: List of composite one bit circuits.
"""
one_bit_comps = []
for c in self.components:
if isinstance(c, LogicGate):
if isinstance(c, TwoInputLogicGate):
continue
elif type(getattr(c, 'a')) == Wire:
elif isinstance(getattr(c, 'a'), Wire):
one_bit_comps.append(c)
else:
one_bit_comps.extend(c.get_one_bit_components())
@ -74,16 +87,16 @@ class ArithmeticCircuit():
return one_bit_comps
def get_multi_bit_components(self):
"""Retrieves list of all multi bit circuits present as subcomponents inside the circuit.
"""Retrieves a list of all the multi bit circuits present as subcomponents inside the circuit.
Returns:
list: List of composite multi bit circuits.
"""
multi_bit_comps = []
for c in self.components:
if isinstance(c, LogicGate):
if isinstance(c, TwoInputLogicGate):
continue
elif type(getattr(c, 'a')) == Wire:
elif isinstance(getattr(c, 'a'), Wire):
continue
else:
multi_bit_comps.append(c)
@ -102,9 +115,9 @@ class ArithmeticCircuit():
return list({type(c): c for c in components}.values())
def get_component_types(self):
"""Retrieves list of all unique types of subcomponents composing the circuit.
"""Retrieves a list of all the unique types of subcomponents composing the circuit.
Returning list consists of unique types of logic gates, one bit circuits and multi bit circuits.
Returning list consists of only the unique types of logic gates, one bit circuits and multi bit circuits.
Returns:
list: List of unique component types describing the circuit.
@ -132,23 +145,50 @@ class ArithmeticCircuit():
"""
return self.out.get_wire(1)
def get_circuit_wires(self):
"""Retrieves all the unique wires used for interconnecting subcomponents inside the circuit and stores them inside `self.circuit_wires` list.
Additionally stores all unique names of just the inner input wires inside `self.inputs` list.
def save_wire_id(self, wire: Wire):
"""Returns appropriate wire index position within the circuit.
Constant wire with value 0 has constant index of 0.
Constant wire with value 1 has constant index of 1.
Other wires indexes start counting from 2 and up.
Args:
wire (Wire): Wire that will be stored at this circuit index position.
Returns:
int: Wire's index position within circuit.
"""
if wire.is_const():
return wire.cgp_const
else:
return len([w[0] for w in self.circuit_wires if w[0].is_const() is False]) + 2
def get_cgp_wires(self):
"""Gets a list of all wires in circuit along with their index position for cgp chromosome generation and stores them inside `self.circuit_wires` list.
Constant wire with value 0 has constant index of 0.
Constant wire with value 1 has constant index of 1.
Other wires indexes start counting from 2 and up.
"""
self.circuit_wires = []
for component in self.components:
if not [item for item in self.circuit_wires if item[1] == component.a.name]:
self.circuit_wires.append((component.a, component.a.name, len(self.circuit_wires)))
if isinstance(self.a, Bus):
[self.circuit_wires.append((w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.a.bus]
[self.circuit_wires.append((w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.b.bus]
else:
self.circuit_wires.append((self.a, f"{self.a.name}", self.save_wire_id(wire=self.a)))
self.circuit_wires.append((self.b, f"{self.b.name}", self.save_wire_id(wire=self.b)))
if hasattr(self, 'c'):
self.circuit_wires.append((self.c, f"{self.c.name}", self.save_wire_id(wire=self.c)))
if not [item for item in self.circuit_wires if item[1] == component.b.name]:
self.circuit_wires.append((component.b, component.b.name, len(self.circuit_wires)))
for gate in self.circuit_gates:
if not [item for item in self.circuit_wires if gate.a.name == item[1]]:
self.circuit_wires.append((gate.a, gate.a.name, self.save_wire_id(wire=gate.a)))
if not [item for item in self.circuit_wires if item[1] == component.out.name]:
self.circuit_wires.append((component.out, component.out.name, len(self.circuit_wires)))
if hasattr(gate, 'b') and not [item for item in self.circuit_wires if gate.b.name == item[1]]:
self.circuit_wires.append((gate.b, gate.b.name, self.save_wire_id(wire=gate.b)))
# Get unique names of all inner input circuits (mainly used in one bit circuits)
self.inputs = [i[0] for i in self.circuit_wires if i[0] not in [o.out for o in self.components]]
if not [item for item in self.circuit_wires if gate.out.name == item[1]]:
self.circuit_wires.append((gate.out, gate.out.name, self.save_wire_id(wire=gate.out)))
def get_circuit_wire_index(self, wire: Wire):
"""Searches for circuit's wire unique index position within the circuit. Used for cgp chromosome generation.
@ -159,46 +199,12 @@ class ArithmeticCircuit():
Returns:
int: Wire's index position number within the circuit.
"""
for w in self.circuit_wires:
if wire.name.endswith(w[1]):
return w[2]
def get_circuit_gates(self):
"""Gets list of all logic gates present in circuit.
Returns:
list: List of composite logic gates.
"""
gates = []
for c in self.components:
if isinstance(c, LogicGate):
gates.append(c)
else:
gates.extend((c.get_circuit_gates()))
return gates
def get_cgp_wires(self):
"""Gets list of all wires in circuit along with their index position for cgp chromosome generation and stores them inside `self.circuit_wires` list.
"""
self.circuit_wires = []
if isinstance(self.a, Bus):
[self.circuit_wires.append((w, f"_{w.name}", len(self.circuit_wires))) for w in self.a.bus]
[self.circuit_wires.append((w, f"_{w.name}", len(self.circuit_wires))) for w in self.b.bus]
if wire.is_const():
return wire.cgp_const
else:
self.circuit_wires.append((self.a, f"_{self.a.name}", len(self.circuit_wires)))
self.circuit_wires.append((self.b, f"_{self.b.name}", len(self.circuit_wires)))
if hasattr(self, 'c'):
self.circuit_wires.append((self.c, f"_{self.c.name}", len(self.circuit_wires)))
for gate in self.circuit_gates:
if not [item for item in self.circuit_wires if gate.a.name.endswith(item[1])]:
self.circuit_wires.append((gate.a, gate.a.name, len(self.circuit_wires)))
if not [item for item in self.circuit_wires if gate.b.name.endswith(item[1])]:
self.circuit_wires.append((gate.b, gate.b.name, len(self.circuit_wires)))
if not [item for item in self.circuit_wires if gate.out.name.endswith(item[1])]:
self.circuit_wires.append((gate.out, gate.out.name, len(self.circuit_wires)))
for w in self.circuit_wires:
if wire.name == w[1]:
return w[2]
""" C CODE GENERATION """
# FLAT C #
@ -219,51 +225,29 @@ class ArithmeticCircuit():
"""
return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.prefix}, {self.c_data_type} {self.b.prefix})" + "{" + "\n"
def get_declarations_c_flat(self):
"""Generates flat C code declaration of input/output buses wires.
Returns:
str: Flattened C code arithmetic circuit's wires declaration.
"""
return f"{self.a.get_wire_declaration_c()}" + \
f"{self.b.get_wire_declaration_c()}" + \
f"".join([c.get_declaration_c_flat() for c in self.components])
# For multi-bit circuit wires declaration
def get_declaration_c_flat(self):
"""Generates flat C code declaration of input/output buses wires of multi-bit arithmetic circuit present as subcomponent in the circuit.
"""Generates flat C code declaration of input/output circuit wires.
Returns:
str: Flattened C code multi-bit arithmetic circuit subcomponent wires declaration.
str: Flat C code arithmetic circuit's wires declaration.
"""
return f"".join([c.get_declaration_c_flat() for c in self.components])
def get_inits_c_flat(self):
"""Generates flat C code initialization and assignment of corresponding arithmetic circuit's input/output buses wires.
Returns:
str: Flattened C code initialization of arithmetic circuit wires.
"""
return f"{self.a.get_wire_assign_c()}" + \
f"{self.b.get_wire_assign_c()}" + \
"".join([c.get_assign_c_flat() if isinstance(c, LogicGate) else c.get_init_c_flat() for c in self.components])
# For multi-bit circuit wires initialization
def get_init_c_flat(self):
"""Generates flat C code initialization and assignment of input/output buses wires of multi-bit arithmetic circuit present as subcomponent in the circuit.
"""Generates flat C code initialization and assignment of corresponding arithmetic circuit's input/output wires.
Returns:
str: Flattened C code multi-bit arithmetic circuit subcomponent wires initialization.
str: Flat C code initialization of arithmetic circuit wires.
"""
return "".join([c.get_assign_c_flat() if isinstance(c, LogicGate) else c.get_init_c_flat() for c in self.components])
return "".join([c.get_assign_c_flat() if isinstance(c, TwoInputLogicGate) else c.get_init_c_flat() for c in self.components])
def get_function_out_c_flat(self):
"""Generates flat C code assignment of corresponding arithmetic circuit's output bus wires.
Returns:
str: Flattened C code containing output bus wires assignment.
str: Flat C code containing output bus wires assignment.
"""
return "".join([f" {self.out.prefix} |= {o.return_wire_value_c(offset=self.out.bus.index(o))};\n" for o in self.out.bus])
return self.out.return_bus_wires_values_c_flat()
# Generating flat C code representation of circuit
def get_c_code_flat(self, file_object):
@ -275,8 +259,8 @@ class ArithmeticCircuit():
file_object.write(self.get_includes_c())
file_object.write(self.get_prototype_c())
file_object.write(self.out.get_declaration_c())
file_object.write(self.get_declarations_c_flat()+"\n")
file_object.write(self.get_inits_c_flat()+"\n")
file_object.write(self.get_declaration_c_flat()+"\n")
file_object.write(self.get_init_c_flat()+"\n")
file_object.write(self.get_function_out_c_flat())
file_object.write(f" return {self.out.prefix}"+";\n}")
file_object.close()
@ -288,7 +272,7 @@ class ArithmeticCircuit():
Returns:
str: Hierarchical C code of all subcomponents function blocks description.
"""
# Add unique component types composing this circuit
# Retrieve all unique component types composing this circuit
self.component_types = self.get_component_types()
return "".join([c.get_function_block_c() for c in self.component_types])
@ -298,49 +282,46 @@ class ArithmeticCircuit():
Returns:
str: Hierarchical C code of multi-bit arithmetic circuit's function block description.
"""
# Obtain proper adder name with its bit width
adder_prefix = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N)
adder_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=adder_prefix)
return f"{adder_block.get_circuit_c()}\n\n"
# Obtain proper circuit name with its bit width
circuit_prefix = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N)
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=circuit_prefix)
return f"{circuit_block.get_circuit_c()}\n\n"
def get_declaration_c_hier(self):
"""Generates hierarchical C code declaration of input/output buses wires.
def get_declarations_c_hier(self):
"""Generates hierarchical C code declaration of input/output circuit wires.
Returns:
str: Hierarchical C code containing unique declaration of arithmetic circuit wires.
"""
return "".join(self.a.get_wire_declaration_c()) + \
"".join(self.b.get_wire_declaration_c()) + \
"".join([c.out.get_declaration_c() if isinstance(c, LogicGate) else c.get_wire_declaration_c_hier() for c in self.components])
return "".join([c.get_declaration_c_hier() for c in self.components])
def get_wire_declaration_c_hier(self):
"""Generates hierarchical C code declaration of corresponding subcomponent input/output buses wires inside the upper component.
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 correspoding function block into inner wires present inside the upper component from which function block has been invoked.
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.prefix}_{self.a.prefix} = 0;\n" + \
f" {self.c_data_type} {self.prefix}_{self.b.prefix} = 0;\n" + \
f" {self.c_data_type} {self.prefix}_{self.out.prefix} = 0;\n" + \
f"{self.out.get_wire_declaration_c()}"
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.out.prefix} = 0;\n"
def get_init_c_hier(self):
"""Generates hierarchical C code initialization and assignment of corresponding arithmetic circuit's input/output buses wires.
"""Generates hierarchical C code initialization and assignment of corresponding arithmetic circuit's input/output wires.
Returns:
str: Hierarchical C code initialization of arithmetic circuit wires.
"""
return f"{self.a.get_wire_assign_c()}" + \
f"{self.b.get_wire_assign_c()}" + \
"".join([f" {c.out.name} = {c.get_gate_invocation_c()}" if isinstance(c, LogicGate) 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(circuit_prefix=self.prefix) for c in self.components])
def get_out_invocation_c(self, circuit_prefix: str):
"""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 correspoding function block into inner wires present inside the upper component from which function block has been invoked.
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.
Args:
circuit_prefix (str): Prefix name of the upper component from which function block is being invoked.
@ -348,13 +329,10 @@ class ArithmeticCircuit():
Returns:
str: Hierarchical C code of subcomponent's C function invocation and output assignment.
"""
# Getting name of circuit type and insitu copying out bus for proper C code generation without affecting actual generated composition
# Getting name of circuit type for proper C code generation without affecting actual generated composition
circuit_type = self.prefix.replace(circuit_prefix+"_", "")
out = Bus(prefix=self.prefix+"_"+self.out.prefix, wires_list=self.out.bus)
return "".join([f" {self.prefix}_{self.a.prefix} |= {w.return_wire_value_c(offset=self.a.bus.index(w))};\n" for w in self.a.bus]) + \
"".join([f" {self.prefix}_{self.b.prefix} |= {w.return_wire_value_c(offset=self.b.bus.index(w))};\n" for w in self.b.bus]) + \
f" {out.prefix} = {circuit_type}({self.prefix}_{self.a.prefix}, {self.prefix}_{self.b.prefix});\n" + \
f"{out.get_wire_assign_c()}"
return self.a.return_bus_wires_values_c_hier() + self.b.return_bus_wires_values_c_hier() + \
f" {self.out.prefix} = {circuit_type}({self.a.prefix}, {self.b.prefix});\n"
def get_function_out_c_hier(self):
"""Generates hierarchical C code assignment of corresponding arithmetic circuit's output bus wires.
@ -362,7 +340,7 @@ class ArithmeticCircuit():
Returns:
str: Hierarchical C code containing output bus wires assignment.
"""
return "".join([f" {self.out.prefix} |= {o.return_wire_value_c(offset=self.out.bus.index(o))};\n" for o in self.out.bus])
return self.out.return_bus_wires_values_c_hier()
def get_circuit_c(self):
"""Generates hierarchical C code subcomponent's function block.
@ -372,7 +350,7 @@ class ArithmeticCircuit():
"""
return f"{self.get_prototype_c()}" + \
f"{self.out.get_declaration_c()}" + \
f"{self.get_declaration_c_hier()}\n" + \
f"{self.get_declarations_c_hier()}\n" + \
f"{self.get_init_c_hier()}\n" + \
f"{self.get_function_out_c_hier()}" + \
f" return {self.out.prefix}"+";\n}"
@ -399,51 +377,29 @@ class ArithmeticCircuit():
"""
return f"module {self.prefix}(input [{self.N-1}:0] {self.a.prefix}, input [{self.N-1}:0] {self.b.prefix}, output [{self.out.N-1}:0] {self.out.prefix});\n"
def get_declarations_v_flat(self):
"""Generates flat Verilog code declaration of input/output buses wires.
Returns:
str: Flattened Verilog code arithmetic circuit's wires declaration.
"""
return f"{self.a.get_wire_declaration_v()}" + \
f"{self.b.get_wire_declaration_v()}" + \
f"".join([c.get_declaration_v_flat() for c in self.components])
# For multi-bit circuit wires declaration
def get_declaration_v_flat(self):
"""Generates flat Verilog code declaration of input/output buses wires of multi-bit arithmetic circuit present as subcomponent in the circuit.
"""Generates flat Verilog code declaration of input/output circuit wires.
Returns:
str: Flattened Verilog code multi-bit arithmetic circuit subcomponent wires declaration.
str: Flat Verilog code arithmetic circuit's wires declaration.
"""
return f"".join([c.get_declaration_v_flat() for c in self.components])
def get_inits_v_flat(self):
def get_init_v_flat(self):
"""Generates flat Verilog code initialization and assignment of corresponding arithmetic circuit's input/output buses wires.
Returns:
str: Flattened Verilog code initialization of arithmetic circuit wires.
str: Flat Verilog code initialization of arithmetic circuit wires.
"""
return f"{self.a.get_wire_assign_v()}" + \
f"{self.b.get_wire_assign_v()}" + \
"".join([c.get_assign_v_flat() if isinstance(c, LogicGate) else c.get_init_v_flat() for c in self.components])
# For multi-bit circuit wires initialization
def get_init_v_flat(self):
"""Generates flat Verilog code initialization and assignment of input/output buses wires of multi-bit arithmetic circuit present as subcomponent in the circuit.
Returns:
str: Flattened Verilog code multi-bit arithmetic circuit subcomponent wires initialization.
"""
return "".join([c.get_assign_v_flat() if isinstance(c, LogicGate) else c.get_init_v_flat() for c in self.components])
return "".join([c.get_assign_v_flat() if isinstance(c, TwoInputLogicGate) else c.get_init_v_flat() for c in self.components])
def get_function_out_v_flat(self):
"""Generates flat Verilog code assignment of corresponding arithmetic circuit's output bus wires.
Returns:
str: Flattened Verilog code containing output bus wires assignment.
str: Flat Verilog code containing output bus wires assignment.
"""
return "".join([f" assign {self.out.prefix}[{self.out.bus.index(o)}] = {o.prefix};\n" for o in self.out.bus])
return self.out.return_bus_wires_values_v_flat()
# Generating flat Verilog code representation of circuit
def get_v_code_flat(self, file_object):
@ -453,8 +409,8 @@ class ArithmeticCircuit():
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_prototype_v())
file_object.write(self.get_declarations_v_flat()+"\n")
file_object.write(self.get_inits_v_flat() + "\n")
file_object.write(self.get_declaration_v_flat()+"\n")
file_object.write(self.get_init_v_flat() + "\n")
file_object.write(self.get_function_out_v_flat())
file_object.write(f"endmodule")
file_object.close()
@ -466,7 +422,7 @@ class ArithmeticCircuit():
Returns:
str: Hierarchical Verilog code of all subcomponents function blocks description.
"""
# Add unique component types composing this circuit
# Retrieve all unique component types composing this circuit
self.component_types = self.get_component_types()
return "".join([c.get_function_block_v() for c in self.component_types])
@ -476,49 +432,46 @@ class ArithmeticCircuit():
Returns:
str: Hierarchical Verilog code of multi-bit arithmetic circuit's function block description.
"""
# Obtain proper adder name with its bit width
adder_prefix = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N)
adder_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=adder_prefix)
return f"{adder_block.get_circuit_v()}\n\n"
# Obtain proper circuit name with its bit width
circuit_prefix = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N)
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=circuit_prefix)
return f"{circuit_block.get_circuit_v()}\n\n"
def get_declaration_v_hier(self):
"""Generates hierarchical Verilog code declaration of input/output buses wires.
def get_declarations_v_hier(self):
"""Generates hierarchical Verilog code declaration of input/output circuit wires.
Returns:
str: Hierarchical Verilog code containing unique declaration of arithmetic circuit wires.
"""
return "".join(self.a.get_wire_declaration_v()) + \
"".join(self.b.get_wire_declaration_v()) + \
"".join([c.out.get_declaration_v() if isinstance(c, LogicGate) else c.get_wire_declaration_v_hier() for c in self.components])
return "".join([c.get_declaration_v_hier() for c in self.components])
def get_wire_declaration_v_hier(self):
"""Generates hierarchical Verilog code declaration of corresponding subcomponent input/output buses wires inside the upper component.
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 correspoding function block into inner wires present inside the upper component from which function block has been invoked.
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.prefix}_{self.a.prefix};\n" + \
f" wire [{self.b.N-1}:0] {self.prefix}_{self.b.prefix};\n" + \
f" wire [{self.out.N-1}:0] {self.prefix}_{self.out.prefix};\n" + \
f"{self.out.get_wire_declaration_v()}"
return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \
f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \
f" wire [{self.out.N-1}:0] {self.out.prefix};\n"
def get_init_v_hier(self):
"""Generates hierarchical Verilog code initialization and assignment of corresponding arithmetic circuit's input/output buses wires.
"""Generates hierarchical Verilog code initialization and assignment of corresponding arithmetic circuit's input/output wires.
Returns:
str: Hierarchical Verilog code initialization of arithmetic circuit wires.
"""
return f"{self.a.get_wire_assign_v()}" + \
f"{self.b.get_wire_assign_v()}" + \
"".join([c.get_gate_invocation_v() if isinstance(c, LogicGate) else c.get_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(circuit_prefix=self.prefix) for c in self.components])
def get_invocation_v(self, circuit_prefix: str):
def get_out_invocation_v(self, circuit_prefix: str):
"""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 correspoding function block into inner wires present inside the upper component from which function block has been invoked.
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.
Args:
circuit_prefix (str): Prefix name of the upper component from which function block is being invoked.
@ -528,11 +481,8 @@ class ArithmeticCircuit():
"""
# Getting name of circuit type and insitu copying out bus for proper Verilog code generation without affecting actual generated composition
circuit_type = self.prefix.replace(circuit_prefix+"_", "")
out = Bus(prefix=self.prefix+"_"+self.out.prefix, wires_list=self.out.bus)
return "".join([f" assign {self.prefix}_{self.a.prefix}[{self.a.bus.index(w)}] = {w.name};\n" for w in self.a.bus]) + \
"".join([f" assign {self.prefix}_{self.b.prefix}[{self.b.bus.index(w)}] = {w.name};\n" for w in self.b.bus]) + \
f" {circuit_type} {circuit_type}_{self.out.prefix}({self.prefix}_{self.a.prefix}, {self.prefix}_{self.b.prefix}, {out.prefix});\n" + \
f"{out.get_wire_assign_v()}"
return self.a.return_bus_wires_values_v_hier() + self.b.return_bus_wires_values_v_hier() + \
f" {circuit_type} {circuit_type}_{self.out.prefix}({self.a.prefix}, {self.b.prefix}, {self.out.prefix});\n"
def get_function_out_v_hier(self):
"""Generates hierarchical Verilog code assignment of corresponding arithmetic circuit's output bus wires.
@ -540,7 +490,7 @@ class ArithmeticCircuit():
Returns:
str: Hierarchical Verilog code containing output bus wires assignment.
"""
return "".join([f" assign {self.out.prefix}[{self.out.bus.index(o)}] = {o.name};\n" for o in self.out.bus])
return self.out.return_bus_wires_values_v_hier()
def get_circuit_v(self):
"""Generates hierarchical Verilog code subcomponent's function block.
@ -549,7 +499,7 @@ class ArithmeticCircuit():
str: Hierarchical Verilog code of subcomponent's function block.
"""
return f"{self.get_prototype_v()}" + \
f"{self.get_declaration_v_hier()}\n" + \
f"{self.get_declarations_v_hier()}\n" + \
f"{self.get_init_v_hier()}\n" + \
f"{self.get_function_out_v_hier()}" + \
f"endmodule"
@ -576,35 +526,35 @@ class ArithmeticCircuit():
return f".model {self.prefix}\n"
def get_declaration_blif(self):
"""Generates flat Blif code declaration of arithmetic circuit's input/output buses wires.
"""Generates flat Blif code declaration of input/output circuit wires.
Returns:
str: Flattened Blif code containing declaration of circuit's interface wires.
str: Flat Blif code containing declaration of circuit's wires.
"""
if self.N == 1:
return f".inputs {self.a.prefix} {self.b.prefix}\n" + \
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
f"{self.a.get_wire().get_assign_blif(name=self.a.prefix)}" + \
f"{self.b.get_wire().get_assign_blif(name=self.b.prefix)}"
f".names vdd\n1\n" + \
f".names gnd\n0\n"
else:
return f".inputs {self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}\n" + \
return f".inputs{self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}\n" + \
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
f"{self.a.get_wire_assign_blif()}" + \
f"{self.b.get_wire_assign_blif()}"
f".names vdd\n1\n" + \
f".names gnd\n0\n"
def get_function_blif_flat(self):
"""Generates flat Blif code with unique input wire mapping and invocation of subcomponents functions via their corresponding truth tables.
"""Generates flat Blif code with invocation of subcomponents logic gates functions via their corresponding truth tables.
Returns:
str: Flattened Blif code containing input wires mapping and inner subcomponents Boolean functions invocation.
str: Flat Blif code containing invocation of inner subcomponents logic gates Boolean functions.
"""
return "".join(c.get_init_function_blif_flat() if isinstance(c, LogicGate) else c.get_function_blif_flat() for c in self.components)
return "".join(c.get_function_blif_flat() for c in self.components)
def get_function_out_blif(self):
"""Generates flat Blif code mapping of corresponding arithmetic circuit's output bus wires.
"""Generates flat Blif code assignment of corresponding arithmetic circuit's output bus wires.
Returns:
str: Flattened Blif code containing output bus wires assignment mapping.
str: Flat Blif code containing output bus wires assignment.
"""
return f"{self.out.get_wire_assign_blif(output=True)}"
@ -623,19 +573,18 @@ class ArithmeticCircuit():
file_object.close()
# HIERARCHICAL BLIF #
def get_function_blif_hier(self):
"""Generates hierarchical Blif code with unique input wire mapping and invocation of subcomponents function blocks.
def get_invocations_blif_hier(self):
"""Generates hierarchical Blif code with invocations of subcomponents function blocks.
Returns:
str: Hierarchical Blif code containing input wires mapping and inner subcomponents function blocks invocation.
str: Hierarchical Blif code containing invocations of inner subcomponents function blocks.
"""
return "".join(c.get_invocation_blif_hier(init=True) if isinstance(c, LogicGate) else c.get_invocation_blif_hier(circuit_prefix=self.prefix) for c in self.components)
return "".join(c.get_invocation_blif_hier(circuit_prefix=self.prefix) for c in self.components)
def get_invocation_blif_hier(self, circuit_prefix: str):
"""Generates hierarchical Blif 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 correspoding function block into inner wires present inside the upper component from which function block has been invoked.
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.
@ -645,12 +594,12 @@ class ArithmeticCircuit():
"""
# Getting name of circuit type for proper Blif code generation without affecting actual generated composition
circuit_type = self.prefix.replace(circuit_prefix+"_", "")
return "".join([w.get_assign_blif(name=self.prefix+'_'+self.a.prefix+f'[{self.a.bus.index(w)}]', output=True)for w in self.a.bus]) + \
"".join([w.get_assign_blif(name=self.prefix+'_'+self.b.prefix+f'[{self.b.bus.index(w)}]', output=True)for w in self.b.bus]) + \
return f"{self.a.get_wire_assign_blif(output=True)}" + \
f"{self.b.get_wire_assign_blif(output=True)}" + \
f".subckt {circuit_type}" + \
"".join([f" a[{self.a.bus.index(w)}]={self.prefix}_{self.a.prefix}[{self.a.bus.index(w)}]" for w in self.a.bus]) + \
"".join([f" b[{self.b.bus.index(w)}]={self.prefix}_{self.b.prefix}[{self.b.bus.index(w)}]" for w in self.b.bus]) + \
"".join([f" out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n"
"".join([f" a[{self.a.bus.index(w)}]={self.a.prefix}[{self.a.bus.index(w)}]" for w in self.a.bus]) + \
"".join([f" b[{self.b.bus.index(w)}]={self.b.prefix}[{self.b.bus.index(w)}]" for w in self.b.bus]) + \
"".join([f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n"
def get_circuit_blif(self):
"""Generates hierarchical Blif code subcomponent's function block.
@ -660,7 +609,7 @@ class ArithmeticCircuit():
"""
return f"{self.get_prototype_blif()}" + \
f"{self.get_declaration_blif()}" + \
f"{self.get_function_blif_hier()}" + \
f"{self.get_invocations_blif_hier()}" + \
f"{self.get_function_out_blif()}" + \
f".end\n"
@ -670,7 +619,8 @@ class ArithmeticCircuit():
Returns:
str: Hierarchical Blif code of all subcomponents function blocks description.
"""
# Add unique component types composing this circuit
# Retrieve all unique component types composing this circuit
# (iterating backwards as opposed to other representations so the top modul is always above its subcomponents)
self.component_types = self.get_component_types()
return "\n".join([c.get_function_block_blif() for c in self.component_types[::-1]])
@ -680,10 +630,10 @@ class ArithmeticCircuit():
Returns:
str: Hierarchical Blif code of multi-bit arithmetic circuit's function block description.
"""
# Obtain proper adder name with its bit width
adder_prefix = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N)
adder_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=adder_prefix)
return f"{adder_block.get_circuit_blif()}"
# Obtain proper circuit name with its bit width
circuit_prefix = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N)
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=circuit_prefix)
return f"{circuit_block.get_circuit_blif()}"
# Generating hierarchical BLIF code representation of circuit
def get_blif_code_hier(self, file_object):
@ -700,8 +650,9 @@ class ArithmeticCircuit():
# FLAT CGP #
def get_parameters_cgp(self):
"""Generates CGP chromosome parameters of corresponding arithmetic circuit.
In total seven parameters represent: total inputs, total outputs, number of rows, number of columns (gates),
number of each gate's inputs, number of each gate's outputs, quality constant value.
In total seven parameters represent: total inputs, total outputs, number of rows, number of columns (gates),
number of each gate's inputs, number of each gate's outputs, quality constant value.
Returns:
str: CGP chromosome parameters of described arithmetic circuit.
@ -709,18 +660,24 @@ class ArithmeticCircuit():
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}}"
def get_triplet_cgp(self):
"""Generates list of logic gate triplets (2 input wires, logic gate function) using wires unique position indexes within the described circuit.
Each triplet represents unique logic gate within the described arithmetic circuit. Besides the contained input wires indexes and gate's inner logic function, an output wire
with incremented index position is also created and remembered to be appropriately driven as an input to another logic gate or as the circuit's output.
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.
Each triplet represents unique logic gate within the described arithmetic circuit. Besides the contained input wires indexes and gate's inner logic function, an output wire
with incremented index position is also created and remembered to be appropriately driven as an input to another logic gate or as the circuit's output.
Constant wire with value 0 has constant index of 0.
Constant wire with value 1 has constant index of 1.
Other wires indexes start counting from 2 and up.
Returns:
str: List of triplets each describing logic function of corresponding two input logic gate and as a whole describe the arithmetic circuit.
"""
self.get_cgp_wires()
return "".join([g.get_triplet_cgp(a_index=self.get_circuit_wire_index(g.a), b_index=self.get_circuit_wire_index(g.b)) for g in self.circuit_gates])
return "".join([g.get_triplet_cgp(a_id=self.get_circuit_wire_index(g.a)) if isinstance(g, OneInputLogicGate) else
g.get_triplet_cgp(a_id=self.get_circuit_wire_index(g.a), b_id=self.get_circuit_wire_index(g.b)) for g in self.circuit_gates])
def get_output_cgp(self):
def get_outputs_cgp(self):
"""Generates list of output wires indexes of described arithmetic circuit from MSB to LSB.
Returns:
@ -736,6 +693,6 @@ class ArithmeticCircuit():
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_parameters_cgp())
file_object.write(self.get_triplet_cgp())
file_object.write(self.get_output_cgp())
file_object.write(self.get_triplets_cgp())
file_object.write(self.get_outputs_cgp())
file_object.close()

View File

@ -3,7 +3,6 @@ from .arithmetic_circuit import (
)
from ariths_gen.one_bit_circuits.logic_gates import (
LogicGate,
AndGate,
NandGate,
OrGate,
@ -15,6 +14,8 @@ from ariths_gen.one_bit_circuits.logic_gates import (
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
@ -23,25 +24,27 @@ import math
class MultiplierCircuit(ArithmeticCircuit):
"""Class represents a general multiplier circuit derived from `ArithmeticCircuit` class.
The __init__ method calls parent class __init__ method which fills some mandatory attributes concerning arithmetic circuit
that are later used for generation into various representations.
"""
def __init__(self):
super().__init__()
""" Array multipliers """
def get_previous_partial_product(self, a_index: int, b_index: int, offset: int = 0):
# Array multipliers
def get_previous_partial_product(self, a_index: int, b_index: int):
"""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.
offset (int, optional): Offset to properly retrieve previous partial product. Defaults to 0.
Returns:
Wire: Previous row's component wire of corresponding pp.
"""
# To get the index of previous row's connecting adder and its generated pp
index = ((b_index-2) * (self.N*2)) + ((self.N-1)+2*(a_index+2)) + offset
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:
@ -51,16 +54,17 @@ class MultiplierCircuit(ArithmeticCircuit):
else:
return self.components[index].get_sum_wire()
""" Dadda multiplier """
# Dadda/Wallace multipliers
@staticmethod
def get_maximum_height(initial_value: int):
"""Used in dadda multipliers to get multiplier's maximum height.
Maximum height sequence as defined here: https://en.wikipedia.org/wiki/Dadda_multiplier
d(j=1) = 2; d(j+1) = floor(1.5*d)
Maximum height sequence as defined here: https://en.wikipedia.org/wiki/Dadda_multiplier
d(j=1) = 2; d(j+1) = floor(1.5*d)
`j` stands for initial stage value
`d` stands for maximum height for current initial stage value
`j` stands for initial stage value
`d` stands for maximum height for current initial stage value
Args:
initial_value (int): Initial algorithms stage value.
@ -78,27 +82,34 @@ class MultiplierCircuit(ArithmeticCircuit):
if d >= initial_value:
return stage, max_height
def init_column_heights(self):
def init_column_heights(self, signed: bool = False):
"""Creates appropriate number of partial product columns along with filling them with corresponding number of bit pairs.
Args:
signed (bool, optional): Specify whether pp columns bit pairs should perform signed multiplication or not. Defaults to False.
Returns:
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 = [self.add_column_wires(column=col, column_index=columns.index(col)) for col in columns]
columns = [self.add_column_wires(column=col, column_index=columns.index(col), signed=signed) for col in columns]
return columns
def add_column_wires(self, column: list, column_index: int):
def add_column_wires(self, column: list, column_index: int, signed: bool):
"""Fills circuit's partial product column with corresponding bit pairs.
Args:
column (list): List representing column of partial product bits.
column_index (int): Index of partial products column.
signed (bool): Specify whether pp columns bit pairs should perform signed multiplication or not.
Returns:
list: Updated column list containing corresponding number of input bit pairs to form proper pp column.
"""
# Adding neccessary number of lists (based on number of bits in the column stored in `column[0]`)
# to column that each represent individual bit pairs for described column (these bit pairs are then combined in AND/NAND gates)
[column.append([]) for _ in range(column[0])]
# Filling column bit pair lists with appropriate bits
if column_index <= self.N-1:
[column[column[0]-index].append(self.a.get_wire(index)) for index in range(0, column[0])]
[column[index+1].append(self.b.get_wire(index)) for index in range(0, column[0])]
@ -106,20 +117,20 @@ class MultiplierCircuit(ArithmeticCircuit):
[column[self.a.N-index].append(self.a.get_wire(index)) for index in range(self.a.N-1, self.a.N-column[0]-1, -1)]
[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)]
# Filling unsigned pp matrix with AND gates
if self.__class__.__name__ == "unsigned_dadda_multiplier" or self.__class__.__name__ == "unsigned_wallace_multiplier":
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)) for i in range(1, len(column))]
# Filling signed pp matrix with AND/NAND gates (based on Baugh-Wooley multiplication algorithm)
# Converting unsigned column pp bit pair lists into AND gates
if 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))]
# Converting signed column pp bit pair lists into AND/NAND gates (based on Baugh-Wooley multiplication algorithm)
else:
# First half of partial product columns contains only AND gates
if column_index < self.N-1 or column_index == self.out.N-2:
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)) 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))]
# Second half of partial product columns contains NAND/AND gates
else:
column[1] = NandGate(a=column[1][0], b=column[1][1], prefix=self.prefix+'_nand_'+str(column[1][0].index)+'_'+str(column[1][1].index))
column[-1] = NandGate(a=column[-1][0], b=column[-1][1], prefix=self.prefix+'_nand_'+str(column[-1][0].index)+'_'+str(column[-1][1].index))
column[1] = NandGate(a=column[1][0], b=column[1][1], prefix=self.prefix+'_nand_'+str(column[1][0].index)+'_'+str(column[1][1].index), parent_component=self)
column[-1] = NandGate(a=column[-1][0], b=column[-1][1], prefix=self.prefix+'_nand_'+str(column[-1][0].index)+'_'+str(column[-1][1].index), parent_component=self)
if len(column[2:-1]) != 0:
column[2:-1] = [AndGate(a=column[i][0], b=column[i][1], prefix=self.prefix+'_and_'+str(column[i][0].index)+'_'+str(column[i][1].index)) for i in range(2, len(column)-1)]
column[2:-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(2, len(column)-1)]
return column
@ -137,9 +148,9 @@ class MultiplierCircuit(ArithmeticCircuit):
def update_column_heights(self, curr_column: int, curr_height_change: int, next_column: int = 0, next_height_change: int = 0):
"""Updates height of desired column and optionally also its subsequent column.
Used within dadda and wallace multipliers to perform gradual reduction of partial product columns through the stages.
Allows to choose the height change to take effect on the chosen column index and optionally also the same for the following
column if it should also be affected.
Used within dadda and wallace multipliers to perform gradual reduction of partial product columns through the stages.
Allows to choose the height change to take effect on the chosen column index and optionally also the same for the following
column if it should also be affected.
Args:
curr_column (int): Current pp column index.
@ -151,10 +162,11 @@ class MultiplierCircuit(ArithmeticCircuit):
if next_column-1 == curr_column:
self.columns[next_column][0] = self.get_column_height(next_column)+next_height_change
def get_column_wire(self, column: int, bit: int):
def add_column_wire(self, column: int, bit: int):
"""Retrieves wire from desired partial product column bit position.
If bit pair is present at the desired position, it is reduced to one wire with AND/NAND gate accordingly.
If bit pair (AND/NAND gate) is present at the desired position, it is reduced and replaced with AND/NAND gate output wire accordingly.
Either former logic gate's output wire or present wire is returned.
Args:
column (int): Partial product column index.
@ -163,17 +175,38 @@ class MultiplierCircuit(ArithmeticCircuit):
Returns:
Wire: Return Wire present at specified position.
"""
if isinstance(self.columns[column][bit], AndGate) or isinstance(self.columns[column][bit], NandGate):
self.add_component(self.columns[column][bit])
# Checks if a logic gate is present at desired column bit position. If so the gate is added to circuit's list of subcomponents,
# and the former logic gates's output bit replaces the gate at desired column bit position. This output wire is also returned to the caller.
if isinstance(self.columns[column][bit+1], AndGate) or isinstance(self.columns[column][bit+1], NandGate):
self.add_component(self.columns[column][bit+1])
return self.get_previous_component(1).out
else:
return self.columns[column][bit]
return self.columns[column][bit+1]
def get_column_wire(self, column: int, bit: int):
"""Retrieves wire from desired partial product column bit position.
If bit pair (AND/NAND gate) is present at the desired position, AND/NAND gate output wire is returned,
if not the wire present at the desired position is returned.
Args:
column (int): Partial product column index.
bit (int): Bit position within the chosen column.
Returns:
Wire: Return Wire present at specified position.
"""
# Checks if a logic gate is present at desired column bit position. If so, its output bit is returned.
if isinstance(self.columns[column][bit+1], AndGate) or isinstance(self.columns[column][bit+1], NandGate):
return self.columns[column][bit+1].out
else:
return self.columns[column][bit+1]
def update_column_wires(self, curr_column: int, adder: ArithmeticCircuit, next_column: int = 0):
"""Provides bit height reduction of the chosen column.
Inserts chosen column's top bits into an `adder` circuit to reduce its bit height.
Generated sum is stored to the bottom of the column and generated carry bit is stored to the top of the next column.
Inserts chosen column's top bits into an `adder` circuit to reduce its bit height.
Generated sum is stored to the bottom of the column and generated carry bit is stored to the top of the next column.
Args:
curr_column (int): Current pp column index.

View File

@ -0,0 +1,6 @@
from .logic_gate_circuit import (
MultipleInputLogicGate,
TwoInputLogicGate,
TwoInputInvertedLogicGate,
OneInputLogicGate
)

View File

@ -0,0 +1,753 @@
from ariths_gen.wire_components.wires import Wire, ConstantWireValue0, ConstantWireValue1
from ariths_gen.wire_components.buses import Bus
import math
# Multiple-input #
class MultipleInputLogicGate():
"""Class representing multiple input logic gate internally composed of corresponding two input logic gates.
Gates are cascaded in a tree like structure to solve fan-in issue concerning multi-input logic gates.
Tree like structure increases propagation delay logarithmically with number of inputs.
```
FUNC
FUNC
FUNC
...
```
Description of the __init__ method.
Args:
a (Bus): Bus containing input wires to form inner composite two input logic gates.
two_input_gate_cls (type): Specific two input logic gate class type that specifies what type of multiple input logic gate the instance represents.
parent_component (object) Object of upper component of which this logic gate is a subcomponent (used for adding composite two input gates into `parent_component`'s list of subcomponents).
prefix (str, optional): Prefix used to name inner composite logic gates. Defaults to "".
"""
def __init__(self, a: Bus, two_input_gate_cls, parent_component: object, prefix: str = ""):
while a.N != 1:
N = math.floor(a.N/2)
out_wires = []
# Creation of composite two input logic gates from bus `a`'s bit pairs and addition of generated blocks outputs for next iteration
for bus_index in range(0, N):
gate = two_input_gate_cls(a=a.get_wire(bus_index), b=a.get_wire(bus_index+N), prefix=prefix+str(parent_component.get_instance_num(cls=two_input_gate_cls, count_disabled_gates=False)), parent_component=parent_component)
parent_component.add_component(gate)
out_wires.append(gate.out)
# In case bus `a` has odd number of wires
if a.N % 2 != 0:
out_wires.append(a.get_wire(-1))
N += 1
# Update bus for next iteration until it contains only one output wire
a = Bus(prefix=a.prefix, N=N, wires_list=out_wires)
self.out = a.get_wire(0)
# Two-input #
class TwoInputLogicGate():
"""Class representing two input logic gates.
```
FUNC
```
Description of the __init__ method.
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "gate".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which logic gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "gate", outid: int = 0, parent_component: object = None):
self.a = a
self.b = b
self.prefix = prefix
self.outid = outid
# To toggle whether logic gate function ought to be generated or to be replaced
# by a constant wire for the sake of more optimized circuit generation
self.disable_generation = False
# Obtaining the caller object to gain access into its `components` list Used for adding NOT gates as a replacement for two input logic gates with constant input (optimalization)
# Also used to obtain caller object's `prefix` name for proper wire names generation of flat/hier representations
self.parent_component = parent_component
""" C CODE GENERATION """
# FLAT C #
@staticmethod
def get_includes_c():
"""Generates necessary C library includes for output representation.
Returns:
str: C code library includes.
"""
return f"#include <stdio.h>\n#include <stdint.h>\n\n"
def get_prototype_c_flat(self):
"""Generates flat C code function header to describe corresponding two input logic gate's interface in flat C code.
Returns:
str: Function's name and parameters in flat C code.
"""
return f"uint8_t {self.prefix}(uint8_t {self.a.name}, uint8_t {self.b.name})" + "{" + "\n"
def get_function_c(self):
"""Generates C code representing corresponding two input logic gate's Boolean function using bitwise operators between its bitwise shifted inputs.
Returns:
str: C code description of logic gate's Boolean function (with bitwise shifted inputs).
"""
return f"{self.a.get_wire_value_c_flat()} {self.operator} {self.b.get_wire_value_c_flat()}"
def get_declaration_c_flat(self):
"""Generates C code declaration of output wire for flat representation.
Returns:
str: C code logic gate's output wire declaration.
"""
# No gate output wire is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f"{self.out.get_declaration_c()}"
def get_assign_c_flat(self):
"""Generates C code for invocation of logical functions and subsequently provides assignment to their output.
Returns:
str: C code invocation of logical function and assignment to output.
"""
# No gate logic is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f" {self.out.prefix} = {self.get_function_c()};\n"
# Generating flat C code representation of the logic gate itself
# (I.e. not as a component of bigger circuit)
def get_c_code(self, file_object):
"""Generates flat C code representation of corresponding logic gate itself.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_includes_c())
file_object.write(self.get_prototype_c_flat())
file_object.write(" return "+(self.get_function_c())+";\n}")
file_object.close()
# HIERARCHICAL C #
def get_prototype_c_hier(self):
"""Generates hierarchical C code function header to describe corresponding two input logic gate's interface in hierarchical C code.
Returns:
str: Function's name and parameters in hierarchical C code.
"""
return f"uint8_t {self.gate_type}(uint8_t {self.a.name}, uint8_t {self.b.name})" + "{" + "\n"
def get_function_block_c(self):
"""Generates C code representation of corresponding logic gate used as a function block in hierarchical circuit description.
Returns:
str: C code of logic gate's function block description.
"""
gate_block = type(self)(a=Wire(name="a"), b=Wire(name="b"))
return f"{gate_block.get_prototype_c_hier()}" + \
f" return "+(gate_block.get_function_c())+";\n}\n\n"
def get_declaration_c_hier(self):
"""Generates C code declaration of output wire for hierarchical representation.
Returns:
str: C code logic gate's output wire declaration.
"""
# No gate output wire is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f"{self.out.get_declaration_c()}"
def get_gate_invocation_c(self):
"""Generates C code invocation of corresponding logic gate's generated function block.
Returns:
str: C code of logic gate's function block invocation.
"""
# No function block is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f" {self.out.name} = {self.gate_type}({self.a.get_wire_value_c_hier()}, {self.b.get_wire_value_c_hier()});\n"
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
def get_prototype_v_flat(self):
"""Generates flat Verilog module header to describe corresponding two input logic gate's interface in flat Verilog.
Returns:
str: Module's name and parameters in flat Verilog.
"""
return f"module {self.prefix}(input {self.a.name}, input {self.b.name}" + \
"".join([f", output {self.out.name}" if self.disable_generation is False else f", output {self.out.name}_out" for _ in range(1)]) + ");\n"
def get_output_v_flat(self):
"""Generates flat Verilog module's output wire assignment used for self logic gate circuit generation.
Returns:
str: Module's output wire assignment in flat Verilog.
"""
return "".join([f" assign {self.out.name} = {self.get_function_v()};\n" if self.disable_generation is False else f" assign {self.out.name}_out = {self.get_function_v()};\n" for _ in range(1)])
def get_declaration_v_flat(self):
"""Generates Verilog code declaration of output wire for flat representation.
Returns:
str: Verilog code logic gate's output wire declaration.
"""
# No gate output wire is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f"{self.out.get_declaration_v_flat()}"
def get_function_v(self):
"""Generates Verilog code representing corresponding two input logic gate's Boolean function using bitwise operators between its inputs.
Returns:
str: Verilog description of logic gate's Boolean function.
"""
return f"{self.a.get_wire_value_v_flat()} {self.operator} {self.b.get_wire_value_v_flat()}"
def get_assign_v_flat(self):
"""Generates Verilog code for invocation of logical functions and subsequently provides assignment to their output.
Returns:
str: Verilog code invocation of logical function and assignment to output.
"""
# No gate logic is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f" assign {self.out.prefix} = {self.get_function_v()};\n"
# Generating flat Verilog code representation of the logic gate itself
# (I.e. not as a component of bigger circuit)
def get_v_code(self, file_object):
"""Generates flat Verilog code representation of corresponding logic gate itself.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_prototype_v_flat())
file_object.write(self.get_output_v_flat())
file_object.write(f"endmodule")
file_object.close()
# HIERARCHICAL VERILOG #
def get_prototype_v_hier(self):
"""Generates hierarchical Verilog module header to describe corresponding two input logic gate's interface in hierarchical Verilog.
Returns:
str: Module's name and parameters in hierarchical Verilog.
"""
return f"module {self.gate_type}(input {self.a.name}, input {self.b.name}, output {self.out.name});\n"
def get_function_block_v(self):
"""Generates Verilog code representation of corresponding logic gate used as function block in hierarchical circuit description.
Returns:
str: Verilog logic gate's function block description.
"""
gate_block = type(self)(a=Wire(name="a"), b=Wire(name="b"), prefix="out")
return f"{gate_block.get_prototype_v_hier()}" + \
f" assign {gate_block.out.name} = {gate_block.get_function_v()};\n" + \
f"endmodule\n\n"
def get_declaration_v_hier(self):
"""Generates Verilog code declaration of output wire for hierarchical representation.
Returns:
str: Verilog code logic gate's output wire declaration.
"""
# No gate output wire is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f"{self.out.get_declaration_v_hier()}"
def get_gate_invocation_v(self):
"""Generates Verilog code invocation of corresponding logic gate's generated function block.
Returns:
str: Verilog code logic gate's function block invocation.
"""
# No function block is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f" {self.gate_type} {self.gate_type}_{self.out.prefix}({self.a.get_wire_value_v_hier()}, {self.b.get_wire_value_v_hier()}, {self.out.prefix});\n"
""" BLIF CODE GENERATION """
# FLAT BLIF #
def get_prototype_blif_flat(self):
"""Generates flat Blif model header to describe corresponding logic gate's interface in flat Blif.
Returns:
str: Model's name in flat Blif code.
"""
return f".model {self.prefix}\n"
def get_declaration_blif(self):
"""Generates Blif code declaration of two input logic gate's wires.
Returns:
str: Blif logic gate's wires declaration.
"""
return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()}\n" + \
f".outputs" + \
"".join([f" {self.out.name}\n" if self.disable_generation is False else f" {self.out.name}_out\n" for _ in range(1)]) + \
f".names vdd\n1\n" + \
f".names gnd\n0\n"
def get_function_blif_flat(self, top_modul: bool = False):
"""Generates Blif code representing corresponding two input logic gate's Boolean function between its inputs.
Invokes corresponding logic gate's `get_function_blif` method for its individual description of logic function.
Args:
top_modul (bool, optional): Specifies whether the described circuit has logic gate as its top modul component (used for self logic gate generation). Defaults to False.
Returns:
str: Blif description of logic gate's Boolean function.
"""
if top_modul is True:
return f"{self.get_function_blif()}"
# No function block is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
elif self.disable_generation:
return ""
else:
return f"{self.get_function_blif()}"
# Generating flat BLIF code representation of the logic gate itself
# (I.e. not as a component of bigger circuit)
def get_blif_code(self, file_object):
"""Generates flat Blif code representation of corresponding logic gate itself.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_prototype_blif_flat())
file_object.write(self.get_declaration_blif())
file_object.write(self.get_function_blif_flat(top_modul=True))
file_object.write(f".end\n")
file_object.close()
# HIERARCHICAL BLIF #
def get_prototype_blif_hier(self):
"""Generates hierarchical Blif model header to describe corresponding logic gate's interface in hierarchical Blif.
Returns:
str: Model's name in hierarchical Blif.
"""
return f".model {self.gate_type}\n"
def get_function_block_blif(self):
"""Generates Blif code representation of corresponding two input logic gate used as subcomponent in hierarchical circuit description.
Returns:
str: Blif logic gate subcomponent description.
"""
gate_block = type(self)(a=Wire(name="a"), b=Wire(name="b"), prefix="out")
return f"{gate_block.get_prototype_blif_hier()}" + \
f"{gate_block.get_declaration_blif()}" + \
f"{gate_block.get_function_blif()}" + \
f".end\n"
def get_invocation_blif_hier(self, top_modul: bool = False, *args, **kwargs):
"""Generates Blif code invocation of corresponding two input logic gate's generated subcomponent.
Args:
top_modul (bool, optional): Specifies whether the described circuit has logic gate as its top modul component (used for self logic gate generation). Defaults to False.
Returns:
str: Blif logic gate subcomponent invocation.
"""
# No function block is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation and top_modul is False:
return ""
else:
return f".subckt {self.gate_type} a={self.a.get_wire_value_blif()} b={self.b.get_wire_value_blif()} out={self.out.get_wire_value_blif()}\n"
""" CGP CODE GENERATION """
# FLAT CGP #
@staticmethod
def get_parameters_cgp():
"""Generates CGP chromosome parameters of corresponding logic gate.
In total seven parameters represent: total inputs, total outputs, number of rows, number of columns (gates),
number of each gate's inputs, number of each gate's outputs, quality constant value.
Returns:
str: CGP chromosome parameters of described logic gate.
"""
return "{2,1,1,1,2,1,0}"
def get_triplet_cgp(self, a_id: int, b_id: int):
"""Generates logic gate triplet (first input wire, second input wire, logic gate function) using wires unique position indexes within the described circuit.
Each triplet represents unique logic gate within the described circuit. Besides the contained input wires indexes and gate's inner logic function, an output wire
with incremented index position is also created and remembered to be appropriately driven as an input to another logic gate or as the circuit's output.
Constant wire with value 0 has constant index of 0.
Constant wire with value 1 has constant index of 1.
Other wires indexes start counting from 2 and up.
Args:
a_id (int): First input wire index position.
b_id (int): Second input wire index position.
Returns:
str: Triplet describing function of corresponding two input logic gate.
"""
return f"({a_id},{b_id},{self.cgp_function})"
@staticmethod
def get_output_cgp(out_id: int):
"""Generates list of output wires indexes of described two input logic gate from MSB to LSB.
Args:
out_id (int): Output wire index position.
Returns:
str: List containing logic gate's output wire indexes (one in this case).
"""
return f"({out_id})"
def get_gate_triplet_cgp(self):
"""Generates flat CGP triplet and output representation of corresponding logic gate itself.
Returns:
str: Triplet and output lists describing function of corresponding two input logic gate.
"""
if self.a.is_const() and self.b.is_const():
a_id = self.a.cgp_const
b_id = self.b.cgp_const
elif self.a.is_const():
a_id = self.a.cgp_const
b_id = 2
elif self.b.is_const():
a_id = 2
b_id = self.b.cgp_const
else:
a_id = 2
b_id = 3
if self.out.is_const():
out_id = self.out.cgp_const
else:
out_id = a_id+1 if a_id > b_id else b_id+1
return self.get_triplet_cgp(a_id=a_id, b_id=b_id) + self.get_output_cgp(out_id=out_id)
def get_cgp_code(self, file_object):
"""Generates flat CGP chromosome representation of corresponding logic gate itself.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_parameters_cgp())
file_object.write(self.get_gate_triplet_cgp())
file_object.close()
class TwoInputInvertedLogicGate(TwoInputLogicGate):
"""Class representing two input inverted logic gates.
```
FUNC
O
```
Description of the __init__ method.
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "gate".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which logic gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "gate", outid: int = 0, parent_component: object = None):
super().__init__(a, b, prefix, outid, parent_component)
""" C CODE GENERATION """
# FLAT C #
def get_function_c(self):
"""Generates C code representing corresponding negated two input logic gate's Boolean function using bitwise operators between its bitwise shifted inputs.
Returns:
str: C code description of negated logic gate's Boolean function (with bitwise shifted inputs).
"""
return "~("+(super().get_function_c()) + ") & 0x01"
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
def get_function_v(self):
"""Generates Verilog code representing corresponding negated two input logic gate's Boolean function using bitwise operators between its inputs.
Returns:
str: Verilog description of negated logic gate's Boolean function.
"""
return "~("+(super().get_function_v())+")"
# One-input #
class OneInputLogicGate(TwoInputLogicGate):
"""Class representing one input logic gates.
```
FUNC
```
Description of the __init__ method.
Args:
a (Wire): First input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "gate".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which logic gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, prefix: str = "gate", outid: int = 0, parent_component: object = None):
self.a = a
self.prefix = prefix
self.outid = outid
# To toggle whether logic gate function ought to be generated or to be replaced
# by a constant wire for the sake of more optimized circuit generation
self.disable_generation = False
# Obtaining the caller object to gain access into its `components` list Used for adding NOT gates as a replacement for two input logic gates with constant input (optimalization)
# Also used to obtain caller object's `prefix` name for proper wire names generation of flat/hier representations
self.parent_component = parent_component
""" C CODE GENERATION """
# FLAT C #
def get_prototype_c_flat(self):
"""Generates flat C code function header to describe corresponding one input logic gate's interface in flat C code.
Returns:
str: Function's name and parameter in flat C code.
"""
return f"uint8_t {self.prefix}(uint8_t {self.a.name})" + "{" + "\n"
def get_function_c(self):
"""Generates C code representing corresponding one input logic gate's Boolean function using bitwise operators between its bitwise shifted input.
Returns:
str: C code description of logic gate's Boolean function (with bitwise shifted input).
"""
return f"{self.operator}({self.a.get_wire_value_c_flat()}) & 0x01"
# HIERARCHICAL C #
def get_prototype_c_hier(self):
"""Generates hierarchical C code function header to describe corresponding one input logic gate's interface in hierarchical C code.
Returns:
str: Function's name and parameters in hierarchical C code.
"""
return f"uint8_t {self.gate_type}(uint8_t {self.a.name})" + "{" + "\n"
def get_function_block_c(self):
"""Generates C code representation of corresponding logic gate used as a function block in hierarchical circuit description.
Returns:
str: C code of logic gate's function block description.
"""
gate_block = type(self)(a=Wire(name="a"))
return f"{gate_block.get_prototype_c_hier()}" + \
f" return "+(gate_block.get_function_c())+";\n}\n\n"
def get_gate_invocation_c(self):
"""Generates C code invocation of corresponding logic gate's generated function block.
Returns:
str: C code of logic gate's function block invocation.
"""
# No function block is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f" {self.out.name} = {self.gate_type}({self.a.get_wire_value_c_hier()});\n"
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
def get_prototype_v_flat(self):
"""Generates flat Verilog module header to describe corresponding one input logic gate's interface in flat Verilog.
Returns:
str: Module's name and parameter in flat Verilog.
"""
return f"module {self.prefix}(input {self.a.name}" + \
"".join([f", output {self.out.name});\n" if self.disable_generation is False else f", output {self.out.name}_out);\n" for _ in range(1)])
def get_function_v(self):
"""Generates Verilog code representing corresponding one input logic gate's Boolean function using bitwise operators between its input.
Returns:
str: Verilog description of logic gate's Boolean function.
"""
return f"{self.operator}{self.a.get_wire_value_v_flat()}"
# HIERARCHICAL VERILOG #
def get_prototype_v_hier(self):
"""Generates hierarchical Verilog module header to describe corresponding one input logic gate's interface in hierarchical Verilog.
Returns:
str: Module's name and parameter in hierarchical Verilog.
"""
return f"module {self.gate_type}(input {self.a.name}, output {self.out.name});\n"
def get_function_block_v(self):
"""Generates Verilog code representation of corresponding logic gate used as a function block in hierarchical circuit description.
Returns:
str: Verilog code of logic gate's function block description.
"""
gate_block = type(self)(a=Wire(name="a"), prefix="out")
return f"{gate_block.get_prototype_v_hier()}" + \
f" assign {gate_block.out.name} = {gate_block.get_function_v()};\n" + \
f"endmodule\n\n"
def get_gate_invocation_v(self):
"""Generates Verilog code invocation of corresponding logic gate's generated function block.
Returns:
str: Verilog code logic gate's function block invocation.
"""
# No function block is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation:
return ""
else:
return f" {self.gate_type} {self.gate_type}_{self.out.prefix}({self.a.get_wire_value_v_hier()}, {self.out.prefix});\n"
""" BLIF CODE GENERATION """
# FLAT BLIF #
def get_declaration_blif(self):
"""Generates Blif code declaration of one input logic gate's wires.
Returns:
str: Blif logic gate's wires declaration.
"""
return f".inputs {self.a.get_declaration_blif()}\n" + \
f".outputs" + \
"".join([f" {self.out.name}\n" if self.disable_generation is False else f" {self.out.name}_out\n" for _ in range(1)]) + \
f".names vdd\n1\n" + \
f".names gnd\n0\n"
# HIERARCHICAL BLIF #
def get_function_block_blif(self):
"""Generates Blif code representation of corresponding one input logic gate used as subcomponent in hierarchical circuit description.
Returns:
str: Blif logic gate subcomponent description.
"""
gate_block = type(self)(a=Wire(name="a"), prefix="out")
return f"{gate_block.get_prototype_blif_hier()}" + \
f"{gate_block.get_declaration_blif()}" + \
f"{gate_block.get_function_blif()}" + \
f".end\n"
def get_invocation_blif_hier(self, top_modul: bool = False, *args, **kwargs):
"""Generates Blif code invocation of corresponding one input logic gate's generated subcomponent.
Args:
top_modul (bool, optional): Specifies whether the described circuit has logic gate as its top modul component (used for self logic gate generation). Defaults to False.
Returns:
str: Blif logic gate subcomponent invocation.
"""
# No function block is generated if one of the inputs is a wire with constant value.
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
if self.disable_generation and top_modul is False:
return ""
else:
return f".subckt {self.gate_type} a={self.a.get_wire_value_blif()} out={self.out.get_wire_value_blif()}\n"
""" CGP CODE GENERATION """
# FLAT CGP #
def get_triplet_cgp(self, a_id: int):
"""Generates logic gate triplet (first input wire, second input wire, logic gate function) using wires unique position indexes within the described circuit.
Each triplet represents unique logic gate within the described circuit. In this case of one input logic gate, the same input wire index is driven to both inputs.
Besides the contained input wires indexes and gate's inner logic function, an output wire with incremented index position is also created and remembered to be
appropriately driven as an input to another logic gate or as the circuit's output.
Constant wire with value 0 has constant index of 0.
Constant wire with value 1 has constant index of 1.
Other wires indexes start counting from 2 and up.
Args:
a_id (int): First (used also as the second) input wire index position.
Returns:
str: Triplet describing function of corresponding one input logic gate.
"""
return f"({a_id},{a_id},{self.cgp_function})"
@staticmethod
def get_output_cgp(out_id: int):
"""Generates list of output wires indexes of described one input logic gate from MSB to LSB.
Args:
out_id (int): Output wire index position.
Returns:
str: List containing logic gate's output wire indexes (one in this case).
"""
return f"({out_id})"
def get_gate_triplet_cgp(self):
"""Generates flat CGP triplet and output representation of corresponding logic gate itself.
Returns:
str: Triplet and output lists describing function of corresponding one input logic gate.
"""
if self.a.is_const():
a_id = self.a.cgp_const
else:
a_id = 2
if self.out.is_const():
out_id = self.out.cgp_const
else:
out_id = a_id+1 if a_id == 2 else 2
return self.get_triplet_cgp(a_id=a_id) + self.get_output_cgp(out_id=out_id)

View File

@ -0,0 +1,7 @@
from .two_input_one_bit_circuit import (
TwoInputOneBitCircuit
)
from .three_input_one_bit_circuit import (
ThreeInputOneBitCircuit
)

View File

@ -0,0 +1,213 @@
from .two_input_one_bit_circuit import (
TwoInputOneBitCircuit
)
from ariths_gen.wire_components.wires import Wire
class ThreeInputOneBitCircuit(TwoInputOneBitCircuit):
"""Class represents a general three input one bit circuit and implements their generation to various representations. It is derived from `TwoInputOneBitCircuit` class.
Description of the __init__ method.
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
c (Wire): Third input wire.
prefix (str, optional): Prefix name of circuit. Defaults to "three_input_one_bit_circuit".
"""
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "three_input_one_bit_circuit"):
super().__init__()
self.c_data_type = "uint8_t"
self.prefix = prefix
self.a = a
self.b = b
self.c = c
""" C CODE GENERATION """
# FLAT C #
# Function prototype with three inputs
def get_prototype_c(self):
"""Generates C code function header to describe corresponding three input one bit 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"
# HIERARCHICAL C #
# Subcomponent generation (three inputs)
def get_out_invocation_c(self, *args, **kwargs):
"""Generates hierarchical C code invocation of corresponding three input one bit circuit's generated function block.
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
component from which function block has been invoked.
Returns:
str: Hierarchical C code subcomponent's C function invocation and output assignment.
"""
# Used to retrieve proper component's output wire offset position within the output bus
output_bus_wire_names = []
[output_bus_wire_names.append(w.prefix) for w in self.out.bus]
circuit_block = self.__class__()
return "".join([f" {c.out.prefix} = ({circuit_block.prefix}({self.a.get_wire_value_c_hier()}, {self.b.get_wire_value_c_hier()}, {self.c.get_wire_value_c_hier()}) >> {output_bus_wire_names.index(c.out.prefix)}) & 0x01;\n" for c in self.components if c.disable_generation is False and c.out.prefix in output_bus_wire_names])
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
# Module prototype with three inputs
def get_prototype_v(self):
"""Generates Verilog for flat module header to describe corresponding three input one bit circuit's interface in Verilog.
It is adapted for generation of general description of three input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner module logic is also modified accordingly. It is used for self three input one bit circuit flat generation.
Returns:
str: Flat module's name and parameters in Verilog.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return f"module {self.prefix}(input {self.a.name}, input {self.b.name}, input {self.c.name}" + \
"".join([f", output {o}" for o in unique_out_wires]) + ");\n"
def get_self_init_v_flat(self):
"""Generates Verilog for self flat module initialization and assignment of corresponding three input one bit circuit's input/output wires.
It is adapted for generation of general description of three input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner module prototype is also modified accordingly. It is used for self three input one bit circuit flat generation.
Returns:
str: Verilog flat module's inner circuit wires initialization and assignment.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return "".join([c.get_assign_v_flat() if c.disable_generation is False else
f" assign {unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))} = {c.out.v_const};\n" if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires and c.out.is_const() else
f" assign {unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))} = {c.out.name};\n" if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
f"" for c in self.components])
# HIERARCHICAL VERILOG #
# Self circuit hierarchical generation
def get_prototype_v_hier(self):
"""Generates Verilog for hierarchical module header to describe corresponding three input one bit circuit's interface in Verilog.
It is adapted for generation of general description of three input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner module logic is also modified accordingly. It is used for self three input one bit circuit hierarchical generation.
Returns:
str: Hierarchical module's name and parameters in Verilog.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return f"module {self.prefix}(input [0:0] {self.a.name}, input [0:0] {self.b.name}, input [0:0] {self.c.name}" + \
"".join([f", output [0:0] {o}" for o in unique_out_wires]) + ");\n"
def get_self_init_v_hier(self):
"""Generates Verilog for hierarchical module's inner initialization and assignment of corresponding arithmetic circuit's input/output wires.
It is adapted for generation of general description of three input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner module prototype is also modified accordingly. It is used for self three input one bit circuit hierarchical generation.
Returns:
str: Verilog hierarchical module's inner circuit wires initialization and assignment.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return "".join([c.get_gate_invocation_v() if c.disable_generation is False else
f" assign {unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}[0] = {c.out.v_const};\n" if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires and c.out.is_const() else
f" assign {unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}[0] = {c.out.name}[0];\n" if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
f"" for c in self.components])
# Subcomponent generation
def get_out_invocation_v(self, *args, **kwargs):
"""Generates hierarchical Verilog code invocation of corresponding three input one bit circuit's generated function block.
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
component from which function block has been invoked.
Returns:
str: Hierarchical Verilog code subcomponent's module invocation and output assignment.
"""
circuit_block = self.__class__()
return f" {circuit_block.prefix} {circuit_block.prefix}_{self.out.prefix}({self.a.get_wire_value_v_hier()}, {self.b.get_wire_value_v_hier()}, {self.c.get_wire_value_v_hier()}{self.out.get_unique_assign_out_wires_v()});\n"
""" BLIF CODE GENERATION """
# FLAT BLIF #
# Model prototype with three inputs
def get_declaration_blif(self):
"""Generates Blif code declaration of three input one bit circuit's input/output wires.
It is adapted for generation of general description of three input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner modul logic is also modified accordingly. It is used for self three input one bit circuit flat/hierarchical generation.
Returns:
str: Blif code containing declaration of circuit's input/output wires.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()} {self.c.get_declaration_blif()}\n" + \
f".outputs" + \
"".join([f" {o}" for o in unique_out_wires]) + "\n" + \
f".names vdd\n1\n" + \
f".names gnd\n0\n"
def get_function_blif_flat(self, top_modul: bool = False):
"""Generates flat Blif code with invocation of subcomponents logic gates Boolean functions via their corresponding truth tables.
It is adapted for generation of general description of three input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner modul prototype is also modified accordingly. It is used for self three input one bit circuit flat generation.
Args:
top_modul (bool, optional): Specifies whether the described circuit represents top modul component (self one bit circuit generation). Defaults to False.
Returns:
str: Flat Blif code containing invocation of inner subcomponents logic gates Boolean functions.
"""
if top_modul:
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return "".join([c.get_function_blif_flat() if c.disable_generation is False else
c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
"" for c in self.components])
else:
return "".join([c.get_function_blif_flat() for c in self.components])
# HIERARCHICAL BLIF #
# Subcomponent generation (3 inputs)
def get_invocations_blif_hier(self):
"""Generates hierarchical Blif code with invocation of subcomponents function blocks.
It is adapted for generation of general description of three input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner modul prototype is also modified accordingly. It is used for self three input one bit circuit hierarchical generation.
Returns:
str: Hierarchical Blif code containing invocation of inner subcomponents function blocks.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return "".join([c.get_invocation_blif_hier() if c.disable_generation is False else
c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
"" for c in self.components])
def get_invocation_blif_hier(self, *args, **kwargs):
"""Generates hierarchical Blif code invocation of corresponding three input one bit circuit's generated function block.
Returns:
str: Hierarchical Blif code subcomponent's model invocation.
"""
circuit_block = self.__class__()
return f".subckt {circuit_block.prefix} {circuit_block.a.prefix}={self.a.get_wire_value_blif()} {circuit_block.b.prefix}={self.b.get_wire_value_blif()} {circuit_block.c.prefix}={self.c.get_wire_value_blif()}{self.out.get_unique_assign_out_wires_blif(function_block_out_bus=circuit_block.out)}\n"
""" CGP CODE GENERATION """
# FLAT CGP #
# Chromosome prototype with three inputs
def get_parameters_cgp(self):
"""Generates CGP chromosome parameters of corresponding three input one bit circuit.
In total seven parameters represent: total inputs, total outputs, number of rows, number of columns (gates),
number of each gate's inputs, number of each gate's outputs, quality constant value.
Returns:
str: CGP chromosome parameters of described circuit.
"""
self.circuit_gates = self.get_circuit_gates()
return f"{{3,{self.out.N},1,{len(self.circuit_gates)},2,1,0}}"

View File

@ -0,0 +1,355 @@
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit
)
from ariths_gen.wire_components.wires import Wire
class TwoInputOneBitCircuit(ArithmeticCircuit):
"""Class represents a general two input one bit circuit and implements their generation to various representations. It is derived from `ArithmeticCircuit` class.
Description of the __init__ method.
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of circuit. Defaults to "two_input_one_bit_circuit".
"""
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "two_input_one_bit_circuit"):
super().__init__()
self.c_data_type = "uint8_t"
self.prefix = prefix
self.a = a
self.b = b
""" C CODE GENERATION """
# FLAT C #
def get_declaration_c_flat(self):
"""Generates flat C code declaration of output wires.
Returns:
str: Flat C code containing unique declaration of circuit wires.
"""
# Unique declaration of all circuit's inner components outputs
return "".join([c.out.get_declaration_c() for c in self.components if c.disable_generation is False])
# Wires values initialization and assignment
def get_init_c_flat(self):
"""Generates flat C code initialization and assignment of corresponding two input one bit circuit's output wires.
Returns:
str: Flat C code initialization of two input one bit circuit output wires.
"""
return "".join([c.get_assign_c_flat() for c in self.components])
# HIERARCHICAL C #
# Subcomponent generation
def get_function_block_c(self):
"""Generates hierarchical C code representation of corresponding two input one bit circuit used as function block in hierarchical circuit description.
Returns:
str: Hierarchical C code of two input one bit circuit's function block description.
"""
adder_block = self.__class__()
return f"{adder_block.get_circuit_c()}\n\n"
def get_out_invocation_c(self, *args, **kwargs):
"""Generates hierarchical C code invocation of corresponding two input one bit circuit's generated function block.
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
component from which function block has been invoked.
Returns:
str: Hierarchical C code subcomponent's C function invocation and output assignment.
"""
# Used to retrieve proper component's output wire offset position within the output bus
output_bus_wire_names = []
[output_bus_wire_names.append(w.prefix) for w in self.out.bus]
circuit_block = self.__class__()
return "".join([f" {c.out.prefix} = ({circuit_block.prefix}({self.a.get_wire_value_c_hier()}, {self.b.get_wire_value_c_hier()}) >> {output_bus_wire_names.index(c.out.prefix)}) & 0x01;\n" for c in self.components if c.disable_generation is False and c.out.prefix in output_bus_wire_names])
# Self circuit hierarchical generation
def get_declaration_c_hier(self):
"""Generates hierarchical C code declaration of output wires.
Returns:
str: Hierarchical C code containing unique declaration of circuit wires.
"""
# Unique declaration of all circuit's inner components outputs
return "".join([c.out.get_declaration_c() for c in self.components if c.disable_generation is False and any(o.prefix == c.out.prefix for o in self.out.bus)])
def get_function_out_c_hier(self):
"""Generates hierarchical C code assignment of corresponding two input one bit circuit's output wires.
Returns:
str: Hierarchical C code containing output bus wires assignment.
"""
return self.out.return_bus_wires_values_c_flat()
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
def get_prototype_v(self):
"""Generates Verilog for flat module header to describe corresponding two input one bit circuit's interface in Verilog.
It is adapted for generation of general description of two input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner module logic is also modified accordingly. It is used for self two input one bit circuit flat generation.
Returns:
str: Flat module's name and parameters in Verilog.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return f"module {self.prefix}(input {self.a.name}, input {self.b.name}" + \
"".join([f", output {o}" for o in unique_out_wires]) + ");\n"
def get_declarations_v_flat(self):
"""Generates flat Verilog code declaration of self one bit circuit wires when described one bit circuit is a top module.
Returns:
str: Flat Verilog code containing unique declaration of one bit circuit wires.
"""
# Unique declaration of all circuit's inner component wires
return "".join([c.out.get_declaration_v_flat() for c in self.components if c.disable_generation is False and not any(o.prefix == c.out.prefix for o in self.out.bus)])
def get_declaration_v_flat(self):
"""Generates flat Verilog code declaration of inner component's output wires.
Returns:
str: Flat Verilog code containing unique declaration of one bit circuit wires.
"""
# Unique declaration of all circuit's inner components outputs
return "".join([c.out.get_declaration_v_flat() for c in self.components if c.disable_generation is False])
# Wires values initialization and assignment
def get_init_v_flat(self):
"""Generates flat Verilog code initialization and assignment of corresponding two input one bit circuit's input/output wires.
Returns:
str: Flat Verilog code initialization of two input one bit circuit wires.
"""
return "".join([c.get_assign_v_flat() for c in self.components])
def get_self_init_v_flat(self):
"""Generates Verilog for self flat module initialization and assignment of corresponding two input one bit circuit's input/output wires.
It is adapted for generation of general description of two input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner module prototype is also modified accordingly. It is used for self two input one bit circuit flat generation.
Returns:
str: Verilog flat module's inner circuit wires initialization and assignment.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return "".join([c.get_assign_v_flat() if c.disable_generation is False else
f" assign {unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))} = {c.out.v_const};\n" if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires and c.out.is_const() else
f" assign {unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))} = {c.out.name};\n" if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
f"" for c in self.components])
# Generating flat Verilog code representation of circuit
def get_v_code_flat(self, file_object):
"""Generates flat Verilog code representation of corresponding one bit circuit.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_prototype_v())
file_object.write(self.get_declarations_v_flat())
file_object.write(self.get_self_init_v_flat())
file_object.write(f"endmodule")
file_object.close()
# HIERARCHICAL VERILOG #
# Subcomponent generation
def get_function_block_v(self):
"""Generates hierarchical Verilog code representation of corresponding two input one bit circuit used as function block in hierarchical circuit description.
Returns:
str: Hierarchical Verilog code of two input one bit circuit's function block description.
"""
adder_block = self.__class__()
return f"{adder_block.get_circuit_v()}\n\n"
def get_out_invocation_v(self, *args, **kwargs):
"""Generates hierarchical Verilog code invocation of corresponding two input one bit circuit's generated function block.
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
component from which function block has been invoked.
Returns:
str: Hierarchical Verilog code subcomponent's module invocation and output assignment.
"""
circuit_block = self.__class__()
return f" {circuit_block.prefix} {circuit_block.prefix}_{self.out.prefix}({self.a.get_wire_value_v_hier()}, {self.b.get_wire_value_v_hier()}{self.out.get_unique_assign_out_wires_v()});\n"
# Self circuit hierarchical generation
def get_prototype_v_hier(self):
"""Generates Verilog for hierarchical module header to describe corresponding two input one bit circuit's interface in Verilog.
It is adapted for generation of general description of two input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner module logic is also modified accordingly. It is used for self two input one bit circuit hierarchical generation.
Returns:
str: Hierarchical module's name and parameters in Verilog.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return f"module {self.prefix}(input [0:0] {self.a.name}, input [0:0] {self.b.name}" + \
"".join([f", output [0:0] {o}" for o in unique_out_wires]) + ");\n"
def get_declarations_v_hier(self):
"""Generates hierarchical Verilog code declaration of input subcomponent's circuit wires.
Returns:
str: Hierarchical Verilog code containing unique declaration of subcomponent's function block circuit wires.
"""
# Unique declaration of all circuit's inner component wires
return "".join([c.out.get_declaration_v_hier() for c in self.components if c.disable_generation is False and not any(o.prefix == c.out.prefix for o in self.out.bus)])
def get_declaration_v_hier(self):
"""Generates hierarchical Verilog code declaration of output wires.
Returns:
str: Hierarchical Verilog code containing unique declaration of circuit wires.
"""
# Unique declaration of all circuit's inner components outputs
return "".join([c.out.get_declaration_v_hier() for c in self.components if c.disable_generation is False and any(o.prefix == c.out.prefix for o in self.out.bus)])
def get_self_init_v_hier(self):
"""Generates Verilog for hierarchical module's inner initialization and assignment of corresponding arithmetic circuit's input/output wires.
It is adapted for generation of general description of two input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner module prototype is also modified accordingly. It is used for self two input one bit circuit hierarchical generation.
Returns:
str: Verilog hierarchical module's inner circuit wires initialization and assignment.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return "".join([c.get_gate_invocation_v() if c.disable_generation is False else
f" assign {unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}[0] = {c.out.v_const};\n" if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires and c.out.is_const() else
f" assign {unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}[0] = {c.out.name}[0];\n" if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
f"" for c in self.components])
def get_circuit_v(self):
"""Generates hierarchical Verilog code subcomponent's function block.
Returns:
str: Hierarchical Verilog code of subcomponent's function block.
"""
return f"{self.get_prototype_v_hier()}" + \
f"{self.get_declarations_v_hier()}" + \
f"{self.get_self_init_v_hier()}" + \
f"endmodule"
""" BLIF CODE GENERATION """
# FLAT BLIF #
def get_declaration_blif(self):
"""Generates Blif code declaration of two input one bit circuit's input/output wires.
It is adapted for generation of general description of two input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner modul logic is also modified accordingly. It is used for self two input one bit circuit flat/hierarchical generation.
Returns:
str: Blif code containing declaration of circuit's input/output wires.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()}\n" + \
f".outputs" + \
"".join([f" {o}" for o in unique_out_wires]) + "\n" + \
f".names vdd\n1\n" + \
f".names gnd\n0\n"
def get_function_blif_flat(self, top_modul: bool = False):
"""Generates flat Blif code with invocation of subcomponents logic gates Boolean functions via their corresponding truth tables.
It is adapted for generation of general description of two input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner modul prototype is also modified accordingly. It is used for self two input one bit circuit flat generation.
Args:
top_modul (bool, optional): Specifies whether the described circuit represents top modul component (self one bit circuit generation). Defaults to False.
Returns:
str: Flat Blif code containing invocation of inner subcomponents logic gates Boolean functions.
"""
if top_modul:
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return "".join([c.get_function_blif_flat() if c.disable_generation is False else
c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
"" for c in self.components])
else:
return "".join([c.get_function_blif_flat() for c in self.components])
# Generating flat BLIF code representation of circuit
def get_blif_code_flat(self, file_object):
"""Generates flat Blif code representation of corresponding one bit circuit.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_prototype_blif())
file_object.write(self.get_declaration_blif())
file_object.write(self.get_function_blif_flat(top_modul=True))
file_object.write(f".end\n")
file_object.close()
# HIERARCHICAL BLIF #
# Subcomponent/self circuit generation
def get_function_block_blif(self):
"""Generates hierarchical Blif code representation of corresponding two input one bit circuit used as function block in hierarchical circuit description.
Returns:
str: Hierarchical Blif code of two input one bit circuit's function block description.
"""
adder_block = self.__class__()
return f"{adder_block.get_circuit_blif()}"
def get_circuit_blif(self):
"""Generates hierarchical Blif code subcomponent's function block.
Returns:
str: Hierarchical Blif code of subcomponent's function block.
"""
return f"{self.get_prototype_blif()}" + \
f"{self.get_declaration_blif()}" + \
f"{self.get_invocations_blif_hier()}" + \
f".end\n"
def get_invocations_blif_hier(self):
"""Generates hierarchical Blif code with invocation of subcomponents function blocks.
It is adapted for generation of general description of two input one bit circuits as well as their modified versions when some inputs are desired as constant values.
In such cases the inner modul prototype is also modified accordingly. It is used for self two input one bit circuit hierarchical generation.
Returns:
str: Hierarchical Blif code containing invocation of inner subcomponents function blocks.
"""
unique_out_wires = []
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) for o in self.out.bus]
return "".join([c.get_invocation_blif_hier() if c.disable_generation is False else
c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
"" for c in self.components])
def get_invocation_blif_hier(self, *args, **kwargs):
"""Generates hierarchical Blif code invocation of corresponding two input one bit circuit's generated function block.
Returns:
str: Hierarchical Blif code subcomponent's model invocation.
"""
circuit_block = self.__class__()
return f".subckt {circuit_block.prefix} {circuit_block.a.prefix}={self.a.get_wire_value_blif()} {circuit_block.b.prefix}={self.b.get_wire_value_blif()}{self.out.get_unique_assign_out_wires_blif(function_block_out_bus=circuit_block.out)}\n"
""" CGP CODE GENERATION """
# FLAT CGP #
def get_parameters_cgp(self):
"""Generates CGP chromosome parameters of corresponding two input one bit circuit.
In total seven parameters represent: total inputs, total outputs, number of rows, number of columns (gates),
number of each gate's inputs, number of each gate's outputs, quality constant value.
Returns:
str: CGP chromosome parameters of described circuit.
"""
self.circuit_gates = self.get_circuit_gates()
return f"{{2,{self.out.N},1,{len(self.circuit_gates)},2,1,0}}"

View File

@ -1,94 +0,0 @@
from .two_input_one_bit_circuit import (
TwoInputOneBitCircuit
)
class ThreeInputOneBitCircuit(TwoInputOneBitCircuit):
"""Class represents a general three input one bit circuit and implements their generation to various representations. It is derived from `TwoInputOneBitCircuit` class.
"""
def __init__(self):
super().__init__()
""" C CODE GENERATION """
# FLAT C #
# Function prototype with three inputs
def get_prototype_c(self):
"""Generates C code function header to describe corresponding three input one bit 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"
# HIERARCHICAL C #
# Subcomponent generation (three inputs)
def get_out_invocation_c(self, **kwargs):
"""Generates hierarchical C code invocation of corresponding three input one bit circuit's generated function block.
Assigns output values from invocation of the correspoding function block into inner wires present inside the upper component from which function block has been invoked.
Returns:
str: Hierarchiacal C code subcomponent's C function invocation and output assignment.
"""
circuit_class = self.__class__()
return "".join([f' {o.name} = ({circuit_class.prefix}({self.a.name}, {self.b.name}, {self.c.name}) >> {self.out.bus.index(o)}) & 0x01;\n' for o in self.out.bus])
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
# Module prototype with three inputs
def get_prototype_v(self):
"""Generates Verilog module header to describe corresponding three input one bit circuit's interface in Verilog.
Returns:
str: Module's name and parameters in Verilog.
"""
return f"module {self.prefix}(input {self.a.name}, input {self.b.name}, input {self.c.name}{''.join([f', output {o.name}' for o in self.out.bus])});\n"
# HIERARCHICAL VERILOG #
# Subcomponent generation (three inputs)
def get_invocation_v(self, **kwargs):
"""Generates hierarchical Verilog code invocation of corresponding three input one bit circuit's generated function block.
Returns:
str: Hierarchiacal Verilog code subcomponent's module invocation.
"""
circuit_class = self.__class__()
return f" {circuit_class.prefix} {circuit_class.prefix}_{self.out.get_wire().name}({self.a.name}, {self.b.name}, {self.c.name}{''.join([f', {o.name}' for o in self.out.bus])});\n"
""" BLIF CODE GENERATION """
# FLAT BLIF #
# Model prototype with three inputs
def get_declaration_blif(self):
"""Generates flat Blif code declaration of three input one bit circuit's input/output wires.
Returns:
str: Flattened Blif code containing declaration of circuit's interface wires.
"""
return f".inputs {self.a.name} {self.b.name} {self.c.name}\n" + \
f".outputs{self.out.get_wire_declaration_blif(array=False)}\n"
# HIERARCHICAL BLIF #
# Subcomponent generation (3 inputs)
def get_invocation_blif_hier(self, **kwargs):
"""Generates hierarchical Blif code invocation of corresponding three input one bit circuit's generated function block.
Returns:
str: Hierarchiacal Blif code subcomponent's model invocation.
"""
circuit_class = self.__class__()
return f"{self.get_wire_mapping_blif()}" + \
f".subckt {circuit_class.prefix} a={self.inputs[0].name} b={self.inputs[1].name} cin={self.inputs[2].name}{''.join([f' {o.name}={self.out.get_wire(circuit_class.out.bus.index(o)).name}' for o in circuit_class.out.bus])}\n"
""" CGP CODE GENERATION """
# FLAT CGP #
# Chromosome prototype with three inputs
def get_parameters_cgp(self):
"""Generates CGP chromosome parameters of corresponding three input one bit circuit.
In total seven parameters represent: total inputs, total outputs, number of rows, number of columns (gates),
number of each gate's inputs, number of each gate's outputs, quality constant value.
Returns:
str: CGP chromosome parameters of described circuit.
"""
self.circuit_gates = self.get_circuit_gates()
return f"{{3,2,1,{len(self.circuit_gates)},2,1,0}}"

View File

@ -1,295 +0,0 @@
from .arithmetic_circuit import (
ArithmeticCircuit
)
class TwoInputOneBitCircuit(ArithmeticCircuit):
"""Class represents a general two input one bit circuit and implements their generation to various representations. It is derived from `ArithmeticCircuit` class.
"""
def __init__(self):
super().__init__()
""" C CODE GENERATION """
# FLAT C #
# Obtaining list of all the unique circuit wires from all contained logic gates
# to ensure non-recurring declaration of same wires
def get_declaration_c_flat(self):
"""Generates flat C code declaration of input/output wires.
Returns:
str: Flattened C code containing unique declaration of circuit wires.
"""
self.get_circuit_wires()
# Unique declaration of all circuit's interconnections
return "".join([c[0].get_declaration_c() for c in self.circuit_wires])
# Wires values initialization and assignment
def get_init_c_flat(self):
"""Generates flat C code initialization and assignment of corresponding two input one bit circuit's input/output wires.
Returns:
str: Flattened C code initialization of two input one bit circuit wires.
"""
return "".join([i.get_assign_c(name=i.get_wire_value_c(name=i.name.replace(self.prefix+"_", ""))) for i in self.inputs]) + \
"".join([f" {c.out.name} = {c.get_init_c_flat()};\n" for c in self.components])
# Generating flat C code representation of circuit
def get_c_code_flat(self, file_object):
"""Generates flat C code representation of corresponding two input one bit circuit.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_includes_c())
file_object.write(self.get_prototype_c())
file_object.write(self.out.get_declaration_c())
file_object.write(self.get_declaration_c_flat()+"\n")
file_object.write(self.get_init_c_flat()+"\n")
file_object.write(self.get_function_out_c_flat())
file_object.write(f" return {self.out.prefix}"+";\n}")
file_object.close()
# HIERARCHICAL C #
# Subcomponent generation
def get_function_block_c(self):
"""Generates hierarchical C code representation of corresponding two input one bit circuit used as function block in hierarchical circuit description.
Returns:
str: Hierarchical C code of two input one bit circuit's function block description.
"""
adder_block = self.__class__()
return f"{adder_block.get_circuit_c()}\n\n"
def get_wire_declaration_c_hier(self):
"""Generates hierarchical C code declaration of corresponding subcomponent output wires inside the upper component.
Generates wires used to connect output values from invocation of the correspoding function block into inner wires present inside the upper component from which function block has been invoked.
Returns:
str: Hierarchical C code of two input one bit circuit's output wires declaration.
"""
return f"{self.out.get_wire_declaration_c()}"
def get_out_invocation_c(self, **kwargs):
"""Generates hierarchical C code invocation of corresponding two input one bit circuit's generated function block.
Assigns output values from invocation of the correspoding function block into inner wires present inside the upper component from which function block has been invoked.
Returns:
str: Hierarchical C code subcomponent's C function invocation and output assignment.
"""
circuit_class = self.__class__()
return "".join([f' {o.name} = ({circuit_class.prefix}({self.a.name}, {self.b.name}) >> {self.out.bus.index(o)}) & 0x01;\n' for o in self.out.bus])
# Self circuit hierarchical generation
def get_declaration_c_hier(self):
"""Generates hierarchical C code declaration of input/output wires.
Returns:
str: Hierarchical C code containing unique declaration of circuit wires.
"""
self.get_circuit_wires()
# Unique declaration of all circuit's interconnections
return "".join([c[0].get_declaration_c() for c in self.circuit_wires])
def get_init_c_hier(self):
"""Generates hierarchical C code initialization and assignment of corresponding two input one bit circuit's input/output wires.
Returns:
str: Hierarchical C code initialization of two input one bit circuit wires.
"""
return "".join([i.get_assign_c(name=i.get_wire_value_c(name=i.name.replace(self.prefix+"_", ""))) for i in self.inputs]) + \
"".join([f" {c.out.name} = {c.get_gate_invocation_c(remove_prefix=False)}" for c in self.components])
def get_function_out_c_hier(self):
"""Generates hierarchical C code assignment of corresponding two input one bit circuit's output wires.
Returns:
str: Hierarchical C code containing output bus wires assignment.
"""
return "".join([f" {self.out.prefix} |= {o.return_wire_value_c(offset=self.out.bus.index(o))};\n" for o in self.out.bus])
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
def get_prototype_v(self):
"""Generates Verilog module header to describe corresponding two input one bit circuit's interface in Verilog.
Returns:
str: Module's name and parameters in Verilog.
"""
return f"module {self.prefix}(input {self.a.name}, input {self.b.name}{''.join([f', output {o.name}' for o in self.out.bus])});\n"
def get_declaration_v_flat(self):
"""Generates flat Verilog code declaration of input/output wires.
Returns:
str: Flattened Verilog code containing unique declaration of circuit wires.
"""
self.get_circuit_wires()
# Unique declaration of all circuit's interconnections
return "".join([c[0].get_declaration_v() for c in self.circuit_wires])
# Wires values initialization and assignment
def get_init_v_flat(self):
"""Generates flat Verilog code initialization and assignment of corresponding two input one bit circuit's input/output wires.
Returns:
str: Flattened Verilog code initialization of two input one bit circuit wires.
"""
return "".join([i.get_assign_v(name=i.name.replace(self.prefix+"_", "")) for i in self.inputs]) + \
"".join([f" assign {c.out.name} = {c.get_init_v_flat()};\n" for c in self.components])
# Generating flat Verilog code representation of circuit
def get_v_code_flat(self, file_object):
"""Generates flat Verilog code representation of corresponding two input one bit circuit.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_prototype_v())
file_object.write(self.get_declaration_v_flat()+"\n")
file_object.write(self.get_init_v_flat())
file_object.write(f"endmodule")
file_object.close()
# HIERARCHICAL VERILOG #
# Subcomponent generation
def get_function_block_v(self):
"""Generates hierarchical Verilog code representation of corresponding two input one bit circuit used as function block in hierarchical circuit description.
Returns:
str: Hierarchical Verilog code of two input one bit circuit's function block description.
"""
adder_block = self.__class__()
return f"{adder_block.get_circuit_v()}\n\n"
def get_wire_declaration_v_hier(self):
"""Generates hierarchical Verilog code declaration of corresponding subcomponent output wires inside the upper component.
Generates wires used to connect output values from invocation of the correspoding function block into inner wires present inside the upper component from which function block has been invoked.
Returns:
str: Hierarchical Verilog code of two input one bit circuit's output wires declaration.
"""
return f"{self.out.get_wire_declaration_v()}"
def get_invocation_v(self, **kwargs):
"""Generates hierarchical Verilog code invocation of corresponding two input one bit circuit's generated function block.
Returns:
str: Hierarchical Verilog code subcomponent's module invocation.
"""
circuit_class = self.__class__()
return f" {circuit_class.prefix} {circuit_class.prefix}_{self.out.get_wire().name}({self.a.name}, {self.b.name}{''.join([f', {o.name}' for o in self.out.bus])});\n"
# Self circuit hierarchical generation
def get_declaration_v_hier(self):
"""Generates hierarchical Verilog code declaration of input/output wires.
Returns:
str: Hierarchical Verilog code containing unique declaration of circuit wires.
"""
self.get_circuit_wires()
# Unique declaration of all circuit's interconnections
return "".join([c[0].get_declaration_v() for c in self.circuit_wires if c[0] not in self.out.bus])
def get_init_v_hier(self):
"""Generates hierarchical Verilog code initialization and assignment of corresponding two input one bit circuit's input/output wires.
Returns:
str: Hierarchical Verilog code initialization of two input one bit circuit wires.
"""
return "".join([i.get_assign_v(name=i.name.replace(self.prefix+"_", "")) for i in self.inputs])
def get_function_out_v_hier(self):
"""Generates hierarchical Verilog code invocation of corresponding two input one bit circuit's inner subcomponents to map their output values into circuit's output wires.
Returns:
str: Hierarchical Verilog code of inner subcomponents invocations and their inner assignment of output wires values.
"""
return "".join([f"{c.get_gate_invocation_v(remove_prefix=False)}" for c in self.components])
""" BLIF CODE GENERATION """
# FLAT BLIF #
def get_declaration_blif(self):
"""Generates flat Blif code declaration of two input one bit circuit's input/output wires.
Returns:
str: Flattened Blif code containing declaration of circuit's interface wires.
"""
return f".inputs {self.a.name} {self.b.name}\n" + \
f".outputs{self.out.get_wire_declaration_blif(array=False)}\n"
def get_wire_mapping_blif(self):
"""Generates flat Blif code used to map the input wires into other inner wires to obtain proper wire names used for interconnections inside the circuit.
Returns:
str: Flattened Blif code containing unique mapping of all circuit's input interconnections.
"""
# For unique mapping of all circuit's input interconnections
self.get_circuit_wires()
return "".join([i.get_assign_blif(name=i.name.replace(self.prefix+"_", "")) for i in self.inputs])
def get_function_blif_flat(self):
"""Generates flat Blif code with unique input wire mapping and invocation of subcomponents Boolean functions via their corresponding truth tables.
Returns:
str: Flattened Blif code containing input wires mapping and inner subcomponents Boolean functions invocation.
"""
return f"{self.get_wire_mapping_blif()}"+"".join([c.get_function_blif_flat() for c in self.components])
# Generating flat BLIF code representation of circuit
def get_blif_code_flat(self, file_object):
"""Generates flat Blif code representation of corresponding two input one bit circuit.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_prototype_blif())
file_object.write(self.get_declaration_blif())
file_object.write(self.get_function_blif_flat())
file_object.write(f".end\n")
file_object.close()
# HIERARCHICAL BLIF #
# Subcomponent generation
def get_function_block_blif(self):
"""Generates hierarchical Blif code representation of corresponding two input one bit circuit used as function block in hierarchical circuit description.
Returns:
str: Hierarchical Blif code of two input one bit circuit's function block description.
"""
adder_block = self.__class__()
return f"{adder_block.get_circuit_blif()}"
def get_invocation_blif_hier(self, **kwargs):
"""Generates hierarchical Blif code invocation of corresponding two input one bit circuit's generated function block.
Returns:
str: Hierarchical Blif code subcomponent's model invocation.
"""
circuit_class = self.__class__()
return f"{self.get_wire_mapping_blif()}" + \
f".subckt {circuit_class.prefix} a={self.inputs[0].name} b={self.inputs[1].name}{''.join([f' {o.name}={self.out.get_wire(circuit_class.out.bus.index(o)).name}' for o in circuit_class.out.bus])}\n"
# Self circuit hierarchical generation
def get_function_blif_hier(self):
"""Generates hierarchical Blif code with unique input wire mapping and invocation of subcomponents function blocks.
Returns:
str: Hierarchical Blif code containing input wires mapping and inner subcomponents function blocks invocation.
"""
return f"{self.get_wire_mapping_blif()}"+"".join(c.get_invocation_blif_hier(init=False) for c in self.components)
""" CGP CODE GENERATION """
# FLAT CGP #
def get_parameters_cgp(self):
"""Generates CGP chromosome parameters of corresponding two input one bit circuit.
In total seven parameters represent: total inputs, total outputs, number of rows, number of columns (gates),
number of each gate's inputs, number of each gate's outputs, quality constant value.
Returns:
str: CGP chromosome parameters of described circuit.
"""
self.circuit_gates = self.get_circuit_gates()
return f"{{2,2,1,{len(self.circuit_gates)},2,1,0}}"

View File

@ -11,4 +11,9 @@ from ariths_gen.multi_bit_circuits.adders.pg_ripple_carry_adder import(
from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import(
UnsignedCarryLookaheadAdder,
SignedCarryLookaheadAdder
)
from ariths_gen.multi_bit_circuits.adders.carry_skip_adder import(
UnsignedCarrySkipAdder,
SignedCarrySkipAdder
)

View File

@ -1,21 +1,23 @@
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
from ariths_gen.core import (
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit,
MultiplierCircuit
)
from ariths_gen.core.logic_gate_circuits import (
MultipleInputLogicGate
)
from ariths_gen.one_bit_circuits.one_bit_components import (
HalfAdder,
PGLogicBlock,
ConstantWireValue0,
ConstantWireValue1,
FullAdder,
FullAdderPG
)
from ariths_gen.one_bit_circuits.logic_gates import (
LogicGate,
AndGate,
NandGate,
OrGate,
@ -27,9 +29,9 @@ from ariths_gen.one_bit_circuits.logic_gates import (
class UnsignedCarryLookaheadAdder(ArithmeticCircuit):
"""Class representing unsigned carry look-ahead adder.
"""Class representing unsigned carry-lookahead adder.
Unsigned carry look-ahead adder represents faster adder circuit which is composed
Unsigned carry-lookahead adder represents faster adder circuit which is composed
of more complex circuitry but provides much less propagation delay as opposed to rca.
It is mainly composed of propagate/generate blocks and many AND/OR gates to calculate carries individually.
@ -58,7 +60,8 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit):
b (Bus): Second input bus.
prefix (str, optional): Prefix name of unsigned cla. Defaults to "u_cla".
"""
def __init__(self, a: Bus, b: Bus, prefix: str = "u_cla"):
def __init__(self, a: Bus, b: Bus, cla_block_size: int = 4, prefix: str = "u_cla"):
#TODO
super().__init__()
self.N = max(a.N, b.N)
self.prefix = prefix
@ -69,88 +72,86 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit):
self.a.bus_extend(N=self.N, prefix=a.prefix)
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Lists containing all propagate/generate wires
self.propagate = []
self.generate = []
# Output wires for N sum bits and additional cout bit
self.out = Bus("out", self.N+1)
self.out = Bus(self.prefix+"_out", self.N+1)
# Constant wire with value 0 for cin 0
constant_wire_0 = ConstantWireValue0(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_0)
# Used as a first generate wire for obtaining next carry bits
self.generate.append(constant_wire_0.out.get_wire())
# To signify current number of blocks and number of bits that remain to be added into function blocks
N_blocks = 0
N_wires = self.N
cin = ConstantWireValue0()
# Gradual addition of propagate/generate logic blocks and AND/OR gates for Cout bits generation, XOR gates for Sum bits generation
for input_index in range(self.N):
pg_block = PGLogicBlock(self.a.get_wire(input_index), self.b.get_wire(input_index), prefix=self.prefix+"_pg_logic"+str(input_index))
self.propagate.append(pg_block.get_propagate_wire())
self.generate.append(pg_block.get_generate_wire())
self.add_component(pg_block)
while N_wires != 0:
# Lists containing all propagate/generate wires
self.propagate = []
self.generate = []
# Cin0 used as a first generate wire for obtaining next carry bits
self.generate.append(cin)
block_size = cla_block_size if N_wires >= cla_block_size else N_wires
if input_index == 0:
obj_sum_xor = XorGate(pg_block.get_sum_wire(), constant_wire_0.out.get_wire(), prefix=self.prefix+"_xor"+str(input_index))
self.add_component(obj_sum_xor)
self.out.connect(input_index, obj_sum_xor.out)
# Gradual addition of propagate/generate logic blocks and AND/OR gates for Cout bits generation, XOR gates for Sum bits generation
for i in range(block_size):
pg_block = PGLogicBlock(self.a.get_wire((N_blocks*cla_block_size)+i), self.b.get_wire((N_blocks*cla_block_size)+i), prefix=self.prefix+"_pg_logic"+str(self.get_instance_num(cls=PGLogicBlock)))
self.propagate.append(pg_block.get_propagate_wire())
self.generate.append(pg_block.get_generate_wire())
self.add_component(pg_block)
# Carry propagation calculation
obj_and = AndGate(self.propagate[input_index], self.generate[input_index], prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)))
self.add_component(obj_and)
if i == 0 and N_blocks == 0:
obj_sum_xor = XorGate(pg_block.get_sum_wire(), cin, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(obj_sum_xor)
self.out.connect(i+(N_blocks*cla_block_size), obj_sum_xor.out)
# Carry bit generation
obj_cout_or = OrGate(pg_block.get_generate_wire(), self.get_previous_component().out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate)))
self.add_component(obj_cout_or)
else:
obj_sum_xor = XorGate(pg_block.get_sum_wire(), self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(input_index))
self.add_component(obj_sum_xor)
self.out.connect(input_index, obj_sum_xor.out)
# Carry propagation calculation
obj_and = AndGate(self.propagate[(N_blocks*cla_block_size)+i], self.generate[(N_blocks*cla_block_size)+i], prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), parent_component=self)
self.add_component(obj_and)
# For each pg pair values algorithmically combine two input AND gates to replace multiple input gates (resolves fan-in issue)
composite_and_gates = []
# And combine AND gate pairs into OR gates
composite_or_gates = []
# Carry bit generation
obj_cout_or = OrGate(pg_block.get_generate_wire(), self.get_previous_component().out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate, count_disabled_gates=False)), parent_component=self)
self.add_component(obj_cout_or)
else:
obj_sum_xor = XorGate(pg_block.get_sum_wire(), self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(obj_sum_xor)
self.out.connect(i+(N_blocks*cla_block_size), obj_sum_xor.out)
# Carry propagation calculation
for g_index in range(len(self.generate)-1):
for p_index in range(g_index, len(self.propagate)):
# No gate to cascade with, add to list
if len(composite_and_gates) == 0:
obj_and = AndGate(self.propagate[p_index], self.generate[g_index], prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)))
# Combine 2 gates into another one to cascade them
else:
# Create new AND gate
obj_and = AndGate(self.propagate[p_index], self.generate[g_index], prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)))
self.add_component(obj_and)
# List of AND gates outputs that are later combined in a multi-bit OR gate
composite_or_gates_inputs = []
# Combine new gate with previous one stored in list
obj_and = AndGate(self.get_previous_component(1).out, self.get_previous_component(2).out, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)))
composite_and_gates.pop(composite_and_gates.index(self.get_previous_component(2)))
for g_index in range(len(self.generate)-1):
composite_wires = []
# Getting a list of wires used for current bit position cout composite AND gate's generation
# E.g. for Cout2 = G1 + G0·P1 C0·P0·P1 it gets a list containing [C0,P0,P1] then [G0,P1]
composite_wires.append(self.generate[g_index])
for p_index in range(len(self.propagate)-1, g_index-1, -1):
composite_wires.append(self.propagate[p_index])
# Add gate to circuit components and to list of composite AND gates for this pg pair value
self.add_component(obj_and)
composite_and_gates.append(obj_and)
# For each pg pair values algorithmically combine two input AND gates to replace multiple input gates (resolves fan-in issue)
pg_wires = Bus(wires_list=composite_wires)
multi_bit_and_gate = MultipleInputLogicGate(a=pg_wires, two_input_gate_cls=AndGate, prefix=self.prefix+"_and", parent_component=self)
composite_or_gates_inputs.append(multi_bit_and_gate.out)
composite_or_gates.append(composite_and_gates.pop())
# Final OR gates cascade using generated AND gates output wires
composite_or_wires = Bus(wires_list=composite_or_gates_inputs)
multi_bit_or_gate = MultipleInputLogicGate(a=composite_or_wires, two_input_gate_cls=OrGate, prefix=self.prefix+"_or", parent_component=self)
# Final OR gates cascade using generated AND gates representing multiple input AND gates (cascade of multiple two input ones)
for a in range(len(composite_or_gates)-1):
obj_or = OrGate(self.get_previous_component().out, composite_or_gates[a].out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate)))
self.add_component(obj_or)
# Carry bit generation
obj_cout_or = OrGate(pg_block.get_generate_wire(), multi_bit_or_gate.out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate, count_disabled_gates=False)), parent_component=self)
self.add_component(obj_cout_or)
# Carry bit generation
obj_cout_or = OrGate(pg_block.get_generate_wire(), self.get_previous_component().out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate)))
self.add_component(obj_cout_or)
# Updating cin for the the next bypass block
# Also updating cout value which is used as cin for the first adder of the next block
cin = obj_cout_or.out
# Connecting last output bit to last cout
if input_index == (self.N-1):
self.out.connect(self.N, obj_cout_or.out)
N_wires -= block_size
N_blocks += 1
# Connection of final Cout
self.out.connect(self.N, cin)
class SignedCarryLookaheadAdder(UnsignedCarryLookaheadAdder, ArithmeticCircuit):
"""Class representing signed carry look-ahead adder.
"""Class representing signed carry-lookahead adder.
Signed carry look-ahead adder represents faster adder circuit which is composed
Signed carry-lookahead adder represents faster adder circuit which is composed
of more complex circuitry but provides much less propagation delay as opposed to rca.
It is mainly composed of propagate/generate blocks and many AND/OR gates to calculate carries individually.
At last XOR gates are used to ensure proper sign extension.
@ -180,13 +181,13 @@ class SignedCarryLookaheadAdder(UnsignedCarryLookaheadAdder, ArithmeticCircuit):
b (Bus): Second input bus.
prefix (str, optional): Prefix name of signed cla. Defaults to "s_cla".
"""
def __init__(self, a: Bus, b: Bus, prefix: str = "s_cla"):
super().__init__(a=a, b=b, prefix=prefix)
def __init__(self, a: Bus, b: Bus, cla_block_size: int = 4, prefix: str = "s_cla"):
super().__init__(a=a, b=b, cla_block_size=cla_block_size, prefix=prefix)
self.c_data_type = "int64_t"
# Additional XOR gates to ensure correct sign extension in case of sign addition
sign_xor_1 = XorGate(self.a.get_wire(self.N-1), self.b.get_wire(self.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)))
sign_xor_1 = XorGate(self.a.get_wire(self.N-1), self.b.get_wire(self.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(sign_xor_1)
sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)))
sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(sign_xor_2)
self.out.connect(self.N, sign_xor_2.out)

View File

@ -0,0 +1,180 @@
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus,
wires
)
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit,
MultiplierCircuit
)
from ariths_gen.core.logic_gate_circuits import (
MultipleInputLogicGate
)
from ariths_gen.one_bit_circuits.one_bit_components import (
HalfAdder,
PGLogicBlock,
FullAdder,
FullAdderPG,
TwoOneMultiplexer
)
from ariths_gen.one_bit_circuits.logic_gates import (
AndGate,
NandGate,
OrGate,
NorGate,
XorGate,
XnorGate,
NotGate
)
class UnsignedCarrySkipAdder(ArithmeticCircuit):
"""Class representing unsigned carry skip (bypass) adder composed of smaller carry bypass blocks of chosen size to reduce propagation delay.
Unsigned carry skip (bypass) adder represents faster adder circuit which is composed
of more complex circuitry but provides much less propagation delay as opposed to rca.
Each carry bypass block is composed of these logic parts:
Propagate XOR gates compute propagate signals of corresponding bit pairs, these signals
are then combined in multiple input AND gate (cascaded two input gates).
Half/full adder cascade represents basic ripple carry adder design for input carry to ripple through them,
additionally these adders compute individual output sum bits.
Finally multiplexer lies at the end of each carry bypass block and is used to propagate block's input carry
if multiple input AND gate output, which serves as select signal, is 1 or to wait for rippling of cout from the block's adders if it is 0.
```TODO
B3 A3 B2 A2 B1 A1 B0 A0
PG PG PG PG
block block block block
G3P3S3 G2P2S2 G1P1S1 G0P0S0
Carry Lookahead logic
Cout S3 S1 S0 S0
```
Description of the __init__ method.
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
bypass_block_size (int, optional): Size of each composite bypass adder block size. Defaults to 4.
prefix (str, optional): Prefix name of unsigned csa. Defaults to "u_csa".
"""
def __init__(self, a: Bus, b: Bus, bypass_block_size: int = 4, prefix: str = "u_csa"):
super().__init__()
self.N = max(a.N, b.N)
self.prefix = prefix
self.a = Bus(prefix=a.prefix, wires_list=a.bus)
self.b = Bus(prefix=b.prefix, wires_list=b.bus)
# 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)
# Output wires for N sum bits and additional cout bit
self.out = Bus(self.prefix+"_out", self.N+1)
# To signify current number of blocks and number of bits that remain to be added into function blocks
N_blocks = 0
N_wires = self.N
cin = ConstantWireValue0()
while N_wires != 0:
propagate_wires = []
block_size = bypass_block_size if N_wires >= bypass_block_size else N_wires
for i in range(block_size):
# Generate propagate wires for corresponding bit pairs
propagate_xor = XorGate(a=self.a.get_wire((N_blocks*bypass_block_size)+i), b=self.b.get_wire((N_blocks*bypass_block_size)+i), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(propagate_xor)
propagate_wires.append(propagate_xor.out)
if N_blocks == 0 and i == 0:
obj_adder = HalfAdder(a=self.a.get_wire((N_blocks*bypass_block_size)+i), b=self.b.get_wire((N_blocks*bypass_block_size)+i), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
else:
obj_adder = FullAdder(a=self.a.get_wire((N_blocks*bypass_block_size)+i), b=self.b.get_wire((N_blocks*bypass_block_size)+i), c=cout, prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
cout = obj_adder.get_carry_wire()
self.add_component(obj_adder)
# Connecting adder's output sum bit to its proper position within the described circuit's output bus
self.out.connect(i+(N_blocks*bypass_block_size), obj_adder.get_sum_wire())
# ANDing of propagate wires, gate's output serves as select signal into 2:1 multiplexer and signifies whether block's input carry should be propagated (thus reducing delay) or not
propagation_and = MultipleInputLogicGate(a=Bus(prefix=self.prefix+f"_propagate_signal{N_blocks}", N=len(propagate_wires), wires_list=propagate_wires), two_input_gate_cls=AndGate, parent_component=self, prefix=self.prefix+f"_and_propagate{N_blocks}")
mux = TwoOneMultiplexer(a=cout, b=cin, c=propagation_and.out, prefix=self.prefix+"_mux2to1"+str(self.get_instance_num(cls=TwoOneMultiplexer)))
self.add_component(mux)
# Updating cin for the the next bypass block
# Also updating cout value which is used as cin for the first adder of the next block
cin = mux.out.get_wire()
cout = mux.out.get_wire()
N_wires -= block_size
N_blocks += 1
# Connection of final Cout
self.out.connect(self.N, cin)
class SignedCarrySkipAdder(UnsignedCarrySkipAdder, ArithmeticCircuit):
"""Class representing signed carry skip (bypass) adder composed of smaller carry bypass blocks of chosen size to reduce propagation delay.
Signed carry skip (bypass) adder represents faster adder circuit which is composed
of more complex circuitry but provides much less propagation delay as opposed to rca.
Each carry bypass block is composed of these logic parts:
Propagate XOR gates compute propagate signals of corresponding bit pairs, these signals
are then combined in multiple input AND gate (cascaded two input gates).
Half/full adder cascade represents basic ripple carry adder design for input carry to ripple through them,
additionally these adders compute individual output sum bits.
Finally multiplexer lies at the end of each carry bypass block and is used to propagate block's input carry
if multiple input AND gate output, which serves as select signal, is 1 or to wait for rippling of cout from the block's adders if it is 0.
At last XOR gates are used to ensure proper sign extension.
```TODO
B3 A3 B2 A2 B1 A1 B0 A0
PG PG PG PG
block block block block
G3P3S3 G2P2S2 G1P1S1 G0P0S0
Carry Lookahead logic
with sign extension
Cout S3 S1 S0 S0
```
Description of the __init__ method.
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
bypass_block_size (int, optional): Size of each composite bypass adder block size. Defaults to 4.
prefix (str, optional): Prefix name of signed csa. Defaults to "s_csa".
"""
def __init__(self, a: Bus, b: Bus, bypass_block_size: int = 4, prefix: str = "s_csa"):
super().__init__(a=a, b=b, bypass_block_size=bypass_block_size, prefix=prefix)
self.c_data_type = "int64_t"
# Additional XOR gates to ensure correct sign extension in case of sign addition
sign_xor_1 = XorGate(self.a.get_wire(self.N-1), self.b.get_wire(self.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate, count_disabled_gates=False)), parent_component=self)
self.add_component(sign_xor_1)
sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).out.get_wire(), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate, count_disabled_gates=False)), parent_component=self)
self.add_component(sign_xor_2)
self.out.connect(self.N, sign_xor_2.out)

View File

@ -1,21 +1,20 @@
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
from ariths_gen.core import (
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit,
MultiplierCircuit
)
from ariths_gen.one_bit_circuits.one_bit_components import (
HalfAdder,
PGLogicBlock,
ConstantWireValue0,
ConstantWireValue1,
FullAdder,
FullAdderPG
)
from ariths_gen.one_bit_circuits.logic_gates import (
LogicGate,
AndGate,
NandGate,
OrGate,
@ -74,23 +73,21 @@ class UnsignedPGRippleCarryAdder(ArithmeticCircuit):
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Output wires for N sum bits and additional cout bit
self.out = Bus("out", self.N+1)
self.out = Bus(self.prefix+"_out", self.N+1)
# Gradual addition of 1-bit adder components
for input_index in range(self.N):
if input_index == 0:
# Constant wire with value 0 for cin 0
constant_wire_0 = ConstantWireValue0(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_0)
obj_fa_cla = FullAdderPG(self.a.get_wire(input_index), self.b.get_wire(input_index), constant_wire_0.out.get_wire(), prefix=self.prefix+"_fa"+str(input_index))
# First full adder with connected constant wire with value 0 as cin 0
obj_pg_fa = FullAdderPG(self.a.get_wire(input_index), self.b.get_wire(input_index), ConstantWireValue0(), prefix=self.prefix+"_pg_fa"+str(input_index))
else:
obj_fa_cla = FullAdderPG(self.a.get_wire(input_index), self.b.get_wire(input_index), self.get_previous_component().out, prefix=self.prefix+"_fa"+str(input_index))
obj_pg_fa = FullAdderPG(self.a.get_wire(input_index), self.b.get_wire(input_index), self.get_previous_component().out, prefix=self.prefix+"_pg_fa"+str(input_index))
self.add_component(obj_fa_cla)
self.out.connect(input_index, obj_fa_cla.get_sum_wire())
self.add_component(obj_pg_fa)
self.out.connect(input_index, obj_pg_fa.get_sum_wire())
obj_and = AndGate(self.get_previous_component().c, self.get_previous_component().get_propagate_wire(), prefix=self.prefix+"_and"+str(input_index))
obj_or = OrGate(obj_and.out, self.get_previous_component().get_generate_wire(), prefix=self.prefix+"_or"+str(input_index))
obj_and = AndGate(self.get_previous_component().c, self.get_previous_component().get_propagate_wire(), prefix=self.prefix+"_and"+str(input_index), parent_component=self)
obj_or = OrGate(obj_and.out, self.get_previous_component().get_generate_wire(), prefix=self.prefix+"_or"+str(input_index), parent_component=self)
self.add_component(obj_and)
self.add_component(obj_or)
@ -141,8 +138,8 @@ class SignedPGRippleCarryAdder(UnsignedPGRippleCarryAdder, ArithmeticCircuit):
self.c_data_type = "int64_t"
# Additional XOR gates to ensure correct sign extension in case of sign addition
sign_xor_1 = XorGate(self.a.get_wire(self.N-1), self.b.get_wire(self.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)))
sign_xor_1 = XorGate(self.a.get_wire(self.N-1), self.b.get_wire(self.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(sign_xor_1)
sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)))
sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(sign_xor_2)
self.out.connect(self.N, sign_xor_2.out)

View File

@ -1,21 +1,20 @@
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
from ariths_gen.core import (
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit,
MultiplierCircuit
)
from ariths_gen.one_bit_circuits.one_bit_components import (
HalfAdder,
PGLogicBlock,
ConstantWireValue0,
ConstantWireValue1,
FullAdder,
FullAdderPG
)
from ariths_gen.one_bit_circuits.logic_gates import (
LogicGate,
AndGate,
NandGate,
OrGate,
@ -65,7 +64,7 @@ class UnsignedRippleCarryAdder(ArithmeticCircuit):
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Output wires for N sum bits and additional cout bit
self.out = Bus("out", self.N+1)
self.out = Bus(self.prefix+"_out", self.N+1)
# Gradual addition of 1-bit adder components
for input_index in range(self.N):
@ -115,8 +114,8 @@ class SignedRippleCarryAdder(UnsignedRippleCarryAdder, ArithmeticCircuit):
self.c_data_type = "int64_t"
# Additional XOR gates to ensure correct sign extension in case of sign addition
sign_xor_1 = XorGate(self.get_previous_component(1).a, self.get_previous_component(1).b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)))
sign_xor_1 = XorGate(self.get_previous_component(1).a, self.get_previous_component(1).b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(sign_xor_1)
sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).get_carry_wire(), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)))
sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).get_carry_wire(), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(sign_xor_2)
self.out.connect(self.N, sign_xor_2.out)

View File

@ -0,0 +1,3 @@
from ariths_gen.multi_bit_circuits.dividers.array_divider import(
ArrayDivider
)

View File

@ -0,0 +1,122 @@
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,
TwoOneMultiplexer,
FullSubtractor
)
from ariths_gen.one_bit_circuits.logic_gates import (
AndGate,
NandGate,
OrGate,
NorGate,
XorGate,
XnorGate,
NotGate
)
class ArrayDivider(ArithmeticCircuit):
"""Class representing array divider.
Array divider performs division between two N bit numbers and stores their
quotient in the output bus (remainder bits are not returned).
Design is based on series of iterative subtractions.
```
0 B2 0 B1 A2 B0
FS FS FS
0 A2
2:1MUX2:1MUX
A1 B0
MX0 B2 MX1 B1
FS FS FS
MX1 A1
2:1MUX2:1MUX
A0 B0
MX2 B2 MX3 B1
FS FS FS
NOT NOT NOT
Q2 Q1 Q0
```
Description of the __init__ method.
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
prefix (str, optional): Prefix name of array divider. Defaults to "arrdiv".
"""
def __init__(self, a: Bus, b: Bus, prefix: str = "arrdiv"):
super().__init__()
self.N = max(a.N, b.N)
self.prefix = prefix
self.a = Bus(prefix=a.prefix, wires_list=a.bus)
self.b = Bus(prefix=b.prefix, wires_list=b.bus)
# 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)
# Output wires for quotient result
self.out = Bus(self.prefix+"_out", self.N)
# Performing series of iterative subtractions
# Gradually shifting the divisor
for a_index in reversed(range(self.N)):
# Series of subtractions to obtain quotient bit
for b_index in range(self.N):
# First full subtractor is formed from divisor's LSB bit (b_index) and divident's MSB bit (a_index)
if b_index == 0:
adder_object = FullSubtractor(a=self.a.get_wire(a_index), b=self.b.get_wire(b_index), c=ConstantWireValue0(), prefix=self.prefix+"_fs"+str(self.get_instance_num(cls=FullSubtractor)))
elif a_index == self.N-1:
adder_object = FullSubtractor(a=ConstantWireValue0(), b=self.b.get_wire(b_index), c=self.get_previous_component().get_borrow_wire(), prefix=self.prefix+"_fs"+str(self.get_instance_num(cls=FullSubtractor)))
else:
adder_object = FullSubtractor(a=self.get_previous_component(self.N+1).out.get_wire(), b=self.b.get_wire(b_index), c=self.get_previous_component().get_borrow_wire(), prefix=self.prefix+"_fs"+str(self.get_instance_num(cls=FullSubtractor)))
self.add_component(adder_object)
# Don't generate multiplexers for divison remainders
if a_index != 0:
for mux_index in range(self.N-1):
mux_object = TwoOneMultiplexer(a=self.get_previous_component(self.N).get_difference_wire(), b=self.get_previous_component(self.N).a, c=self.get_previous_component(1+mux_index).get_borrow_wire(), prefix=self.prefix+"_mux2to1"+str(self.get_instance_num(cls=TwoOneMultiplexer)))
self.add_component(mux_object)
# Every borrow out obtained from each iteration of subtractions needs to be negated to represent the quotient output bit
quotient = NotGate(a=adder_object.get_borrow_wire(), prefix=self.prefix+"_not"+str(self.get_instance_num(cls=NotGate)), parent_component=self)
self.add_component(quotient)
self.out.connect(a_index, quotient.out)

View File

@ -1,20 +1,19 @@
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
from ariths_gen.core import (
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit,
MultiplierCircuit
)
from ariths_gen.one_bit_circuits.one_bit_components import (
HalfAdder,
ConstantWireValue0,
ConstantWireValue1,
FullAdder,
FullAdderPG
)
from ariths_gen.one_bit_circuits.logic_gates import (
LogicGate,
AndGate,
NandGate,
OrGate,
@ -53,7 +52,7 @@ class UnsignedArrayMultiplier(MultiplierCircuit):
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Output wires for multiplication product
self.out = Bus("out", self.N*2)
self.out = Bus(self.prefix+"_out", self.N*2)
# Gradual generation of partial products
for b_multiplier_index in range(self.N):
@ -87,10 +86,7 @@ class UnsignedArrayMultiplier(MultiplierCircuit):
# 1 bit multiplier case
if a_multiplicand_index == self.N-1:
constant_wire_0 = ConstantWireValue0(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_0)
self.out.connect(a_multiplicand_index+1, constant_wire_0.out.get_wire())
self.out.connect(a_multiplicand_index+1, ConstantWireValue0)
elif b_multiplier_index == self.N-1:
self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire())
@ -128,29 +124,21 @@ class SignedArrayMultiplier(MultiplierCircuit):
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Output wires for multiplication product
self.out = Bus("out", self.N*2)
# Generating wire with constant logic value 1
constant_wire_1 = ConstantWireValue1(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_1)
# To adjust proper wire connection between adders and AND/NAND gates
# we add offset equal to first block in circuits components list (used for generation of wire with constant value 1)
components_offset = 1
self.out = Bus(self.prefix+"_out", self.N*2)
# Gradual generation of partial products
for b_multiplier_index in range(self.N):
for a_multiplicand_index in range(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))
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))
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 != 0:
previous_product = self.components[a_multiplicand_index + b_multiplier_index + components_offset].out if b_multiplier_index == 1 else self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index, offset=components_offset)
previous_product = self.components[a_multiplicand_index + b_multiplier_index].out if b_multiplier_index == 1 else self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index)
# HA generation for first 1-bit adder in each row starting from the second one
if a_multiplicand_index == 0:
obj_adder = HalfAdder(self.get_previous_component().out, previous_product, prefix=self.prefix+"_ha"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
@ -160,8 +148,9 @@ class SignedArrayMultiplier(MultiplierCircuit):
# 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 == 1:
previous_product = constant_wire_1.out.get_wire()
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)
@ -172,7 +161,7 @@ class SignedArrayMultiplier(MultiplierCircuit):
# 1 bit multiplier case
if a_multiplicand_index == self.N-1:
obj_nor = NorGate(constant_wire_1.out.get_wire(), self.get_previous_component().out, prefix=self.prefix+"_nor_zero_extend")
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)
@ -181,7 +170,7 @@ class SignedArrayMultiplier(MultiplierCircuit):
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(), constant_wire_1.out.get_wire(), prefix=self.prefix+"_xor"+str(a_multiplicand_index+1)+"_"+str(b_multiplier_index))
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)

View File

@ -1,21 +1,20 @@
from ariths_gen.multi_bit_circuits.adders.ripple_carry_adder import UnsignedRippleCarryAdder
from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import UnsignedCarryLookaheadAdder
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
from ariths_gen.core import (
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit,
MultiplierCircuit
)
from ariths_gen.one_bit_circuits.one_bit_components import (
HalfAdder,
ConstantWireValue0,
ConstantWireValue1,
FullAdder,
FullAdderPG
)
from ariths_gen.one_bit_circuits.logic_gates import (
LogicGate,
AndGate,
NandGate,
OrGate,
@ -31,6 +30,10 @@ class UnsignedDaddaMultiplier(MultiplierCircuit):
Unsigned dadda multiplier represents fast N-bit multiplier which utilizes
the functionality of reduction algorithm proposed by Luigi Dadda.
First partial products are calculated for each bit pair that form the partial product multiplication columns.
At last the reduced pairs are inserted into chosen multi bit unsigned adder to execute their summation and obtain the final output bits.
Dadda algorithm is described more in detail here:
https://en.wikipedia.org/wiki/Dadda_multiplier
@ -42,10 +45,10 @@ class UnsignedDaddaMultiplier(MultiplierCircuit):
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
prefix (str, optional): Prefix name of unsigned dadda multiplier. Defaults to "u_dadda_rca".
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedRippleCarryAdder.
prefix (str, optional): Prefix name of unsigned dadda multiplier. Defaults to "u_dadda_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 = "u_dadda_rca", unsigned_adder_class_name: str = UnsignedRippleCarryAdder):
def __init__(self, a: Bus, b: Bus, prefix: str = "u_dadda_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder):
super().__init__()
self.N = max(a.N, b.N)
self.prefix = prefix
@ -57,7 +60,7 @@ class UnsignedDaddaMultiplier(MultiplierCircuit):
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Output wires for multiplication product
self.out = Bus("out", self.N*2)
self.out = Bus(self.prefix+"_out", self.N*2)
# 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))
@ -69,8 +72,8 @@ class UnsignedDaddaMultiplier(MultiplierCircuit):
col = 0
while col < len(self.columns):
if self.get_column_height(col) == self.d + 1:
# Add half adder and also AND gates if neccesarry (via get_column_wire invocation) into list of circuit components
obj_adder = HalfAdder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
# Add half adder and also AND gates if neccesarry (via add_column_wire invocation) into list of circuit components
obj_adder = HalfAdder(self.add_column_wire(column=col, bit=0), self.add_column_wire(column=col, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_adder)
# Update the number of current and next column wires
@ -82,8 +85,8 @@ class UnsignedDaddaMultiplier(MultiplierCircuit):
self.update_column_wires(curr_column=col, next_column=col+1, adder=self.get_previous_component(1))
elif self.get_column_height(col) > self.d:
# Add full adder and also AND gates if neccesarry (via get_column_wire invocation) into list of circuit components
obj_adder = FullAdder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), self.get_column_wire(column=col, bit=3), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
# Add full adder and also AND gates if neccesarry (via add_column_wire invocation) into list of circuit components
obj_adder = FullAdder(self.add_column_wire(column=col, bit=0), self.add_column_wire(column=col, bit=1), self.add_column_wire(column=col, bit=2), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
self.add_component(obj_adder)
# Update the number of current and next column wires
@ -102,34 +105,32 @@ class UnsignedDaddaMultiplier(MultiplierCircuit):
# Output generation
# First output bit from single first pp AND gate
self.out.connect(0, self.get_column_wire(column=0, bit=1))
self.out.connect(0, self.add_column_wire(column=0, bit=0))
# Final addition of remaining bits
# 1 bit multiplier case
if self.N == 1:
constant_wire_0 = ConstantWireValue0(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_0)
self.out.connect(1, constant_wire_0.out.get_wire())
self.out.connect(1, ConstantWireValue0())
# 2 bit multiplier case
elif self.N == 2:
obj_ha = HalfAdder(self.get_column_wire(column=1, bit=1), self.get_column_wire(column=1, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
obj_ha = HalfAdder(self.add_column_wire(column=1, bit=0), self.add_column_wire(column=1, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_ha)
self.out.connect(1, obj_ha.get_sum_wire())
obj_ha = HalfAdder(self.get_previous_component().get_carry_wire(), self.get_column_wire(column=2, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
obj_ha = HalfAdder(self.get_previous_component().get_carry_wire(), self.add_column_wire(column=2, bit=0), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_ha)
self.out.connect(2, obj_ha.get_sum_wire())
self.out.connect(3, obj_ha.get_carry_wire())
# Final addition of remaining bits using chosen unsigned multi bit adder
else:
# Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
adder_prefix = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1)
adder_prefix = self.prefix + "_" + unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1)
adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))])
adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))])
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix+f"_{adder_prefix}")
adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))])
adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))])
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=adder_prefix)
self.add_component(final_adder)
[self.out.connect(o, final_adder.out.get_wire(o-1)) for o in range(1, len(self.out.bus))]
[self.out.connect(o, final_adder.out.get_wire(o-1), inserted_wire_desired_index=o-1) for o in range(1, len(self.out.bus))]
class SignedDaddaMultiplier(MultiplierCircuit):
@ -138,6 +139,11 @@ class SignedDaddaMultiplier(MultiplierCircuit):
Signed dadda multiplier represents fast N-bit multiplier which utilizes
the functionality of reduction algorithm proposed by Luigi Dadda and uses Baugh-Wooley algorithm
to perform signed multiplication.
First partial products are calculated for each bit pair that form the partial product multiplication columns.
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.
Dadda algorithm is described more in detail here:
https://en.wikipedia.org/wiki/Dadda_multiplier
@ -149,10 +155,10 @@ class SignedDaddaMultiplier(MultiplierCircuit):
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
prefix (str, optional): Prefix name of signed dadda multiplier. Defaults to "s_dadda_rca".
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedRippleCarryAdder.
prefix (str, optional): Prefix name of signed dadda multiplier. Defaults to "s_dadda_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 = "s_dadda_rca", unsigned_adder_class_name: str = UnsignedRippleCarryAdder):
def __init__(self, a: Bus, b: Bus, prefix: str = "s_dadda_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder):
super().__init__()
self.N = max(a.N, b.N)
self.prefix = prefix
@ -164,23 +170,18 @@ class SignedDaddaMultiplier(MultiplierCircuit):
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Output wires for multiplication product
self.out = Bus("out", self.N*2)
self.out = Bus(self.prefix+"_out", self.N*2)
# 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))
# Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication
self.columns = self.init_column_heights()
self.columns = self.init_column_heights(signed=True)
# Generating wire with constant logic value 1 for signed multiplication
# Based on Baugh-Wooley multiplication algorithm
# Not used for 1 bit multiplier
if self.N != 1:
constant_wire_1 = ConstantWireValue1(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_1)
# Adding constant wire with value 1 to achieve signedness
# Adding constant wire with value 1 to achieve signedness based on Baugh-Wooley multiplication algorithm
# (adding constant value bit to last column (with one bit) to combine them in XOR gate to get the correct final multplication output bit at the end)
self.columns[self.N].insert(1, constant_wire_1.out.get_wire())
self.columns[self.N].insert(1, ConstantWireValue1())
self.update_column_heights(curr_column=self.N, curr_height_change=1)
# Perform reduction until stage 0
@ -188,8 +189,8 @@ class SignedDaddaMultiplier(MultiplierCircuit):
col = 0
while col < len(self.columns):
if self.get_column_height(col) == self.d + 1:
# Add half adder and also AND/NAND gates if neccesarry (via get_column_wire invocation) into list of circuit components
obj_adder = HalfAdder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
# Add half adder and also AND/NAND gates if neccesarry (via add_column_wire invocation) into list of circuit components
obj_adder = HalfAdder(self.add_column_wire(column=col, bit=0), self.add_column_wire(column=col, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_adder)
# Update the number of current and next column wires
@ -200,8 +201,8 @@ class SignedDaddaMultiplier(MultiplierCircuit):
# add ha's generated cout to the top of next column
self.update_column_wires(curr_column=col, next_column=col+1, adder=self.get_previous_component(1))
elif self.get_column_height(col) > self.d:
# Add full adder and also AND/NAND gates if neccesarry (via get_column_wire invocation) into list of circuit components
obj_adder = FullAdder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), self.get_column_wire(column=col, bit=3), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
# Add full adder and also AND/NAND gates if neccesarry (via add_column_wire invocation) into list of circuit components
obj_adder = FullAdder(self.add_column_wire(column=col, bit=0), self.add_column_wire(column=col, bit=1), self.add_column_wire(column=col, bit=2), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
self.add_component(obj_adder)
# Update the number of current and next column wires
@ -219,37 +220,36 @@ class SignedDaddaMultiplier(MultiplierCircuit):
# Output generation
# First output bit from single first pp AND gate
self.out.connect(0, self.get_column_wire(column=0, bit=1))
self.out.connect(0, self.add_column_wire(column=0, bit=0))
# Final addition of remaining bits
# 1 bit multiplier case (no sign extension)
if self.N == 1:
constant_wire_0 = ConstantWireValue0(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_0)
self.out.connect(1, constant_wire_0.out.get_wire())
self.out.connect(1, ConstantWireValue0())
return
# 2 bit multiplier case
elif self.N == 2:
obj_ha = HalfAdder(self.get_column_wire(column=1, bit=1), self.get_column_wire(column=1, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
obj_ha = HalfAdder(self.add_column_wire(column=1, bit=0), self.add_column_wire(column=1, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_ha)
self.out.connect(1, obj_ha.get_sum_wire())
obj_ha = HalfAdder(self.get_previous_component().get_carry_wire(), self.get_column_wire(column=2, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_ha)
self.out.connect(2, obj_ha.get_sum_wire())
self.out.connect(3, obj_ha.get_carry_wire())
obj_fa = FullAdder(self.get_previous_component().get_carry_wire(), self.add_column_wire(column=2, bit=0), self.add_column_wire(column=2, bit=1), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
self.add_component(obj_fa)
self.out.connect(2, obj_fa.get_sum_wire())
self.out.connect(3, obj_fa.get_carry_wire())
# Final addition of remaining bits using chosen unsigned multi bit adder
else:
# Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
adder_prefix = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1)
adder_prefix = self.prefix + "_" + unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1)
adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))])
adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))])
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix+f"_{adder_prefix}")
adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))])
adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))])
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=adder_prefix)
self.add_component(final_adder)
[self.out.connect(o, final_adder.out.get_wire(o-1)) for o in range(1, len(self.out.bus))]
[self.out.connect(o, final_adder.out.get_wire(o-1), inserted_wire_desired_index=o-1) for o in range(1, len(self.out.bus))]
# Final XOR to ensure proper sign extension
obj_xor = XorGate(constant_wire_1.out.get_wire(), self.out.get_wire(self.out.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)))
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)

View File

@ -1,21 +1,20 @@
from ariths_gen.multi_bit_circuits.adders.ripple_carry_adder import UnsignedRippleCarryAdder
from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import UnsignedCarryLookaheadAdder
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
from ariths_gen.core import (
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit,
MultiplierCircuit
)
from ariths_gen.one_bit_circuits.one_bit_components import (
HalfAdder,
ConstantWireValue0,
ConstantWireValue1,
FullAdder,
FullAdderPG
)
from ariths_gen.one_bit_circuits.logic_gates import (
LogicGate,
AndGate,
NandGate,
OrGate,
@ -31,6 +30,10 @@ class UnsignedWallaceMultiplier(MultiplierCircuit):
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 columns.
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
@ -41,10 +44,10 @@ class UnsignedWallaceMultiplier(MultiplierCircuit):
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
prefix (str, optional): Prefix name of unsigned wallace multiplier. Defaults to "u_wallace_rca".
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedRippleCarryAdder.
prefix (str, optional): Prefix name of unsigned wallace multiplier. Defaults to "u_wallace_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 = "u_wallace_rca", unsigned_adder_class_name: str = UnsignedRippleCarryAdder):
def __init__(self, a: Bus, b: Bus, prefix: str = "u_wallace_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder):
super().__init__()
self.N = max(a.N, b.N)
self.prefix = prefix
@ -56,7 +59,7 @@ class UnsignedWallaceMultiplier(MultiplierCircuit):
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Output wires for multiplication product
self.out = Bus("out", self.N*2)
self.out = Bus(self.prefix+"_out", self.N*2)
# Initialize all columns partial products forming AND gates matrix
self.columns = self.init_column_heights()
@ -67,8 +70,8 @@ class UnsignedWallaceMultiplier(MultiplierCircuit):
while col < len(self.columns):
# If column has exactly 3 bits in height and all previous columns has maximum of 2 bits in height, combine them in a half adder
if self.get_column_height(col) == 3 and all(height <= 2 for (height, *_) in self.columns[0:col-1]):
# Add half adder and also AND gates if neccesarry (via get_column_wire invocation) into list of circuit components
obj_adder = HalfAdder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
# Add half adder and also AND gates if neccesarry (via add_column_wire invocation) into list of circuit components
obj_adder = HalfAdder(self.add_column_wire(column=col, bit=0), self.add_column_wire(column=col, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_adder)
# Update the number of current and next column wires
@ -81,8 +84,8 @@ class UnsignedWallaceMultiplier(MultiplierCircuit):
# If column has more than 3 bits in height, combine them in a full adder
elif self.get_column_height(col) > 3:
# Add full adder and also AND gates if neccesarry (via get_column_wire invocation) into list of circuit components
obj_adder = FullAdder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), self.get_column_wire(column=col, bit=3), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
# Add full adder and also AND gates if neccesarry (via add_column_wire invocation) into list of circuit components
obj_adder = FullAdder(self.add_column_wire(column=col, bit=0), self.add_column_wire(column=col, bit=1), self.add_column_wire(column=col, bit=2), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
self.add_component(obj_adder)
# Update the number of current and next column wires
@ -96,34 +99,32 @@ class UnsignedWallaceMultiplier(MultiplierCircuit):
# Output generation
# First output bit from single first pp AND gate
self.out.connect(0, self.get_column_wire(column=0, bit=1))
self.out.connect(0, self.add_column_wire(column=0, bit=0))
# Final addition of remaining bits
# 1 bit multiplier case
if self.N == 1:
constant_wire_0 = ConstantWireValue0(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_0)
self.out.connect(1, constant_wire_0.out.get_wire())
self.out.connect(1, ConstantWireValue0())
# 2 bit multiplier case
elif self.N == 2:
obj_ha = HalfAdder(self.get_column_wire(column=1, bit=1), self.get_column_wire(column=1, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
obj_ha = HalfAdder(self.add_column_wire(column=1, bit=0), self.add_column_wire(column=1, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_ha)
self.out.connect(1, obj_ha.get_sum_wire())
obj_ha = HalfAdder(self.get_previous_component().get_carry_wire(), self.get_column_wire(column=2, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
obj_ha = HalfAdder(self.get_previous_component().get_carry_wire(), self.add_column_wire(column=2, bit=0), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_ha)
self.out.connect(2, obj_ha.get_sum_wire())
self.out.connect(3, obj_ha.get_carry_wire())
# Final addition of remaining bits using chosen unsigned multi bit adder
else:
# Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
adder_prefix = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1)
adder_prefix = self.prefix + "_" + unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1)
adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))])
adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))])
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix+f"_{adder_prefix}")
adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))])
adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))])
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=adder_prefix)
self.add_component(final_adder)
[self.out.connect(o, final_adder.out.get_wire(o-1)) for o in range(1, len(self.out.bus))]
[self.out.connect(o, final_adder.out.get_wire(o-1), inserted_wire_desired_index=o-1) for o in range(1, len(self.out.bus))]
class SignedWallaceMultiplier(MultiplierCircuit):
@ -132,6 +133,11 @@ class SignedWallaceMultiplier(MultiplierCircuit):
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 columns.
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
@ -142,10 +148,10 @@ class SignedWallaceMultiplier(MultiplierCircuit):
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
prefix (str, optional): Prefix name of signed wallace multiplier. Defaults to "s_wallace_rca".
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedRippleCarryAdder.
prefix (str, optional): Prefix name of signed wallace multiplier. Defaults to "s_wallace_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 = "s_wallace_rca", unsigned_adder_class_name: str = UnsignedRippleCarryAdder):
def __init__(self, a: Bus, b: Bus, prefix: str = "s_wallace_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder):
super().__init__()
self.N = max(a.N, b.N)
self.prefix = prefix
@ -157,21 +163,16 @@ class SignedWallaceMultiplier(MultiplierCircuit):
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Output wires for multiplication product
self.out = Bus("out", self.N*2)
self.out = Bus(self.prefix+"_out", self.N*2)
# Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication
self.columns = self.init_column_heights()
self.columns = self.init_column_heights(signed=True)
# Generating wire with constant logic value 1 for signed multiplication
# Based on Baugh-Wooley multiplication algorithm
# Not used for 1 bit multiplier
if self.N != 1:
constant_wire_1 = ConstantWireValue1(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_1)
# Adding constant wire with value 1 to achieve signedness
# Adding constant wire with value 1 to achieve signedness based on Baugh-Wooley multiplication algorithm
# (adding constant value bit to last column (with one bit) to combine them in XOR gate to get the correct final multplication output bit at the end)
self.columns[self.N].insert(1, constant_wire_1.out.get_wire())
self.columns[self.N].insert(1, ConstantWireValue1())
self.update_column_heights(curr_column=self.N, curr_height_change=1)
# Perform reduction until all columns have 2 or less bits in them
@ -180,8 +181,8 @@ class SignedWallaceMultiplier(MultiplierCircuit):
while col < len(self.columns):
# If column has exactly 3 bits in height and all previous columns has maximum of 2 bits in height, combine them in a half adder
if self.get_column_height(col) == 3 and all(height <= 2 for (height, *_) in self.columns[0:col-1]):
# Add half adder and also AND/NAND gates if neccesarry (via get_column_wire invocation) into list of circuit components
obj_adder = HalfAdder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
# Add half adder and also AND/NAND gates if neccesarry (via add_column_wire invocation) into list of circuit components
obj_adder = HalfAdder(self.add_column_wire(column=col, bit=0), self.add_column_wire(column=col, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_adder)
# Update the number of current and next column wires
@ -194,8 +195,8 @@ class SignedWallaceMultiplier(MultiplierCircuit):
# If column has more than 3 bits in height, combine them in a full adder
elif self.get_column_height(col) > 3:
# Add full adder and also AND/NAND gates if neccesarry (via get_column_wire invocation) into list of circuit components
obj_adder = FullAdder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), self.get_column_wire(column=col, bit=3), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
# Add full adder and also AND/NAND gates if neccesarry (via add_column_wire invocation) into list of circuit components
obj_adder = FullAdder(self.add_column_wire(column=col, bit=0), self.add_column_wire(column=col, bit=1), self.add_column_wire(column=col, bit=2), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
self.add_component(obj_adder)
# Update the number of current and next column wires
@ -209,37 +210,36 @@ class SignedWallaceMultiplier(MultiplierCircuit):
# Output generation
# First output bit from single first pp AND gate
self.out.connect(0, self.get_column_wire(column=0, bit=1))
self.out.connect(0, self.add_column_wire(column=0, bit=0))
# Final addition of remaining bits
# 1 bit multiplier case
if self.N == 1:
constant_wire_0 = ConstantWireValue0(self.a.get_wire(), self.b.get_wire())
self.add_component(constant_wire_0)
self.out.connect(1, constant_wire_0.out.get_wire())
self.out.connect(1, ConstantWireValue0())
return
# 2 bit multiplier case
elif self.N == 2:
obj_ha = HalfAdder(self.get_column_wire(column=1, bit=1), self.get_column_wire(column=1, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
obj_ha = HalfAdder(self.add_column_wire(column=1, bit=0), self.add_column_wire(column=1, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_ha)
self.out.connect(1, obj_ha.get_sum_wire())
obj_ha = HalfAdder(self.get_previous_component().get_carry_wire(), self.get_column_wire(column=2, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
self.add_component(obj_ha)
self.out.connect(2, obj_ha.get_sum_wire())
self.out.connect(3, obj_ha.get_carry_wire())
obj_fa = FullAdder(self.get_previous_component().get_carry_wire(), self.add_column_wire(column=2, bit=0), self.add_column_wire(column=2, bit=1), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))
self.add_component(obj_fa)
self.out.connect(2, obj_fa.get_sum_wire())
self.out.connect(3, obj_fa.get_carry_wire())
# Final addition of remaining bits using chosen unsigned multi bit adder
else:
# Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
adder_prefix = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1)
adder_prefix = self.prefix + "_" + unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1)
adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))])
adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))])
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix+f"_{adder_prefix}")
adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))])
adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))])
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=adder_prefix)
self.add_component(final_adder)
[self.out.connect(o, final_adder.out.get_wire(o-1)) for o in range(1, len(self.out.bus))]
[self.out.connect(o, final_adder.out.get_wire(o-1), inserted_wire_desired_index=o-1) for o in range(1, len(self.out.bus))]
# Final XOR to ensure proper sign extension
obj_xor = XorGate(constant_wire_1.out.get_wire(), self.out.get_wire(self.out.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)))
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)

View File

@ -1,5 +1,4 @@
from .logic_gates import (
LogicGate,
AndGate,
NandGate,
OrGate,

View File

@ -1,369 +1,10 @@
from ariths_gen.wire_components import Wire
from ariths_gen.core.logic_gate_circuits import TwoInputLogicGate, TwoInputInvertedLogicGate, OneInputLogicGate
from ariths_gen.wire_components import Wire, ConstantWireValue0, ConstantWireValue1
# Two-input #
class LogicGate():
"""Class representing two input logic gates.
```
FUNC
```
Description of the __init__ method.
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "gate".
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "gate"):
self.a = Wire(name=prefix+"_"+a.name.replace(prefix+"_", ""), value=a.value)
self.b = Wire(name=prefix+"_"+b.name.replace(prefix+"_", ""), value=b.value)
self.prefix = prefix
""" C CODE GENERATION """
# FLAT C #
@staticmethod
def get_includes_c():
"""Generates necessary C library includes for output representation.
Returns:
str: C code library includes.
"""
return f"#include <stdio.h>\n#include <stdint.h>\n\n"
def get_prototype_c(self):
"""Generates C code function header to describe corresponding two input logic gate's interface in C code.
Returns:
str: Function's name and parameters in C code.
"""
return f"uint8_t {self.gate_type}(uint8_t {self.a.name}, uint8_t {self.b.name})" + "{" + "\n"
def get_function_c(self):
"""Generates C code representing corresponding two input logic gate's Boolean function using bitwise operators between its bitwise shifted inputs.
Returns:
str: C code description of logic gate's Boolean function (with bitwise shifted inputs).
"""
return f"{self.a.get_wire_value_c()} {self.operator} {self.b.get_wire_value_c()}"
def get_declaration_c_flat(self):
"""Generates C code declaration of input/output wires.
Returns:
str: C code logic gate's wires declaration.
"""
return f"{self.a.get_declaration_c()}{self.b.get_declaration_c()}{self.out.get_declaration_c()}"
def get_init_c_flat(self):
"""Generates C code representing corresponding two input logic gate's Boolean function using bitwise operators between its inputs.
Returns:
str: C code description of logic gate's Boolean function.
"""
return f"{self.a.name} {self.operator} {self.b.name}"
def get_assign_c_flat(self):
"""Generates C code for assigning initial values into logic gate's wires.
Returns:
str: C code initialization of wires values.
"""
return f"{self.a.get_assign_c(name=self.a.name.replace(self.prefix+'_', ''))}" + \
f"{self.b.get_assign_c(name=self.b.name.replace(self.prefix+'_', ''))}" + \
f" {self.out.prefix} = {self.get_init_c_flat()};\n"
# Generating flat C code representation of the logic gate itself
# (i.e. not as a component of bigger circuit)
def get_c_code(self, file_object):
"""Generates flat C code representation of corresponding logic gate itself.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_includes_c())
file_object.write(self.get_prototype_c())
file_object.write(" return "+(self.get_function_c())+";\n}")
file_object.close()
# HIERARCHICAL C #
def get_function_block_c(self):
"""Generates C code representation of corresponding logic gate used as function block in hierarchical circuit description.
Returns:
str: C code logic gate's function block description.
"""
gate_block = NotGate(a=Wire(name="a")) if isinstance(self, NotGate) else type(self)(a=Wire(name="a"), b=Wire(name="b"))
return f"{gate_block.get_prototype_c()}" + \
f" return "+(gate_block.get_function_c())+";\n}\n\n"
def get_gate_invocation_c(self, remove_prefix: bool = True):
"""Generates C code invocation of corresponding logic gate's generated function block.
Args:
remove_prefix (bool, optional): Specifies whether function block's input parameter names should contain also its prefix (parent circuit name) or not. Defaults to True.
Returns:
str: C code logic gate's function block invocation.
"""
a_name = self.a.name.replace(self.prefix+"_", "") if remove_prefix is True else self.a.name
b_name = self.b.name.replace(self.prefix+"_", "") if remove_prefix is True else self.b.name
return f"{self.gate_type}({a_name}, {b_name});\n"
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
def get_prototype_v(self):
"""Generates Verilog module header to describe corresponding two input logic gate's interface in Verilog.
Returns:
str: Module's name and parameters in Verilog.
"""
return f"module {self.gate_type}(input {self.a.name}, input {self.b.name}, output {self.out.name});\n"
def get_declaration_v_flat(self):
"""Generates Verilog code declaration of input/output wires.
Returns:
str: Verilog code logic gate's wires declaration.
"""
return f"{self.a.get_declaration_v()}{self.b.get_declaration_v()}{self.out.get_declaration_v()}"
def get_init_v_flat(self):
"""Generates Verilog code representing corresponding two input logic gate's Boolean function using bitwise operators between its inputs.
Returns:
str: Verilog description of logic gate's Boolean function.
"""
return f"{self.a.name} {self.operator} {self.b.name}"
def get_assign_v_flat(self):
"""Generates Verilog code for assigning initial values into logic gate's wires.
Returns:
str: Verilog code initialization of wires values.
"""
return f"{self.a.get_assign_v(name=self.a.name.replace(self.prefix+'_', ''))}" + \
f"{self.b.get_assign_v(name=self.b.name.replace(self.prefix+'_', ''))}" + \
f" assign {self.out.prefix} = {self.get_init_v_flat()};\n"
# Generating flat Verilog code representation of the logic gate itself
# (i.e. not as a component of bigger circuit)
def get_v_code(self, file_object):
"""Generates flat Verilog code representation of corresponding logic gate itself.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_prototype_v())
file_object.write(f" assign {self.out.name} = {self.get_init_v_flat()};\n")
file_object.write(f"endmodule")
file_object.close()
# HIERARCHICAL VERILOG #
def get_function_block_v(self):
"""Generates Verilog code representation of corresponding logic gate used as function block in hierarchical circuit description.
Returns:
str: Verilog logic gate's function block description.
"""
gate_block = NotGate(a=Wire(name="a")) if isinstance(self, NotGate) else type(self)(a=Wire(name="a"), b=Wire(name="b"))
return f"{gate_block.get_prototype_v()}" + \
f" assign {gate_block.out.name} = {gate_block.get_init_v_flat()};\n" + \
f"endmodule\n\n"
def get_gate_invocation_v(self, remove_prefix: bool = True):
"""Generates Verilog code invocation of corresponding logic gate's generated function block.
Args:
remove_prefix (bool, optional): Specifies whether function block's input parameter names contain also its prefix (parent circuit name) or not. Defaults to True.
Returns:
str: Verilog code logic gate's function block invocation.
"""
a_name = self.a.name.replace(self.prefix+"_", "") if remove_prefix is True else self.a.name
b_name = self.b.name.replace(self.prefix+"_", "") if remove_prefix is True else self.b.name
return f" {self.gate_type} {self.gate_type}_{self.out.name}({a_name}, {b_name}, {self.out.name});\n"
""" BLIF CODE GENERATION """
# FLAT BLIF #
def get_prototype_blif(self):
"""Generates Blif model header to describe corresponding logic gate's interface in Blif.
Returns:
str: Model's name and parameters in Blif.
"""
return f".model {self.gate_type}\n"
def get_declaration_blif(self):
"""Generates Blif code declaration of input/output wires.
Returns:
str: Blif logic gate's wires declaration.
"""
return f".inputs {self.a.name} {self.b.name}\n" + \
f".outputs {self.out.name}\n"
def get_init_function_blif_flat(self):
"""Generates Blif code representing corresponding two input logic gate's Boolean function between its inputs.
Returns:
str: Blif description of logic gate's Boolean function.
"""
return f"{self.a.get_assign_blif(name=self.a.name.replace(self.prefix+'_', ''))}" + \
f"{self.b.get_assign_blif(name=self.b.name.replace(self.prefix+'_', ''))}" + \
f"{self.get_function_blif_flat()}"
# Generating flat BLIF code representation of the logic gate itself
# (i.e. not as a component of bigger circuit)
def get_blif_code(self, file_object):
"""Generates flat Blif code representation of corresponding logic gate itself.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_prototype_blif())
file_object.write(self.get_declaration_blif())
file_object.write(self.get_function_blif_flat())
file_object.write(f".end\n")
file_object.close()
# HIERARCHICAL BLIF #
def get_function_block_blif(self):
"""Generates Blif code representation of corresponding logic gate used as subcomponent in hierarchical circuit description.
Returns:
str: Blif logic gate subcomponent description.
"""
gate_block = NotGate(a=Wire(name="a")) if isinstance(self, NotGate) else type(self)(a=Wire(name="a"), b=Wire(name="b"))
return f"{gate_block.get_prototype_blif()}" + \
f"{gate_block.get_declaration_blif()}" + \
f"{gate_block.get_function_blif_flat()}" + \
f".end\n"
def get_invocation_blif_hier(self, init: bool = False):
"""Generates Blif code invocation of corresponding logic gate's generated subcomponent.
Args:
init (bool, optional): Specifies whether subcomponent's input wires initializiation is neccessary before its invocation. Defaults to False.
Returns:
str: Blif logic gate subcomponent invocation.
"""
if init is True:
return f"{self.a.get_assign_blif(name=self.a.name.replace(self.prefix+'_', ''))}" + \
f"{self.b.get_assign_blif(name=self.b.name.replace(self.prefix+'_', ''))}" + \
f".subckt {self.gate_type} _a={self.a.name} _b={self.b.name} _y0={self.out.name}\n"
else:
return f".subckt {self.gate_type} _a={self.a.name} _b={self.b.name} _y0={self.out.name}\n"
""" CGP CODE GENERATION """
# FLAT CGP #
@staticmethod
def get_parameters_cgp():
"""Generates CGP chromosome parameters of corresponding logic gate.
In total seven parameters represent: total inputs, total outputs, number of rows, number of columns (gates),
number of each gate's inputs, number of each gate's outputs, quality constant value.
Returns:
str: CGP chromosome parameters of described logic gate.
"""
return "{2,1,1,1,2,1,0}"
def get_triplet_cgp(self, a_index: int = 0, b_index: int = 1):
"""Generates logic gate triplet (2 input wires, logic gate function) using wires unique position indexes within the described circuit.
Each triplet represents unique logic gate within the described circuit. Besides the contained input wires indexes and gate's inner logic function, an output wire
with incremented index position is also created and remembered to be appropriately driven as an input to another logic gate or as the circuit's output.
Args:
a_index (int, optional): First input wire index position. Defaults to 0.
b_index (int, optional): Second input wire index position. Defaults to 1.
Returns:
str: Triplet describing function of corresponding two input logic gate.
"""
return f"({a_index},{b_index},{self.cgp_function})"
@staticmethod
def get_output_cgp(out_index: int = 2):
"""Generates list of output wires indexes of described two input logic gate from MSB to LSB.
Args:
out_index (int, optional): Output wire index. Defaults to 2.
Returns:
str: List of logic gate's output wire indexes.
"""
return f"({out_index})"
def get_cgp_code(self, file_object):
"""Generates flat CGP chromosome representation of corresponding logic gate itself.
Args:
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
"""
file_object.write(self.get_parameters_cgp())
file_object.write(self.get_triplet_cgp())
file_object.write(self.get_output_cgp())
file_object.close()
class InvertedLogicGate(LogicGate):
"""Class representing two input inverted logic gates.
```
FUNC
O
```
Description of the __init__ method.
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "gate".
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "gate"):
super().__init__(a, b, prefix)
""" C CODE GENERATION """
# FLAT C #
def get_function_c(self):
"""Generates C code representing corresponding negated two input logic gate's Boolean function using bitwise operators between its bitwise shifted inputs.
Returns:
str: C code description of negated logic gate's Boolean function (with bitwise shifted inputs).
"""
return "~("+(super().get_function_c())+") & 0x01 << 0"
def get_init_c_flat(self):
"""Generates C code representing corresponding negated two input logic gate's Boolean function using bitwise operators between its inputs.
Returns:
str: C code description of negated logic gate's Boolean function.
"""
return "~("+(super().get_init_c_flat())+")"
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
def get_init_v_flat(self):
"""Generates Verilog code representing corresponding negated two input logic gate's Boolean function using bitwise operators between its inputs.
Returns:
str: Verilog description of negated logic gate's Boolean function.
"""
return "~("+(super().get_init_v_flat())+")"
class AndGate(LogicGate):
"""Class representing two input and gate.
class AndGate(TwoInputLogicGate):
"""Class representing two input AND gate.
```
@ -378,29 +19,51 @@ class AndGate(LogicGate):
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "".
prefix (str, optional): Prefix name of AND gate. Defaults to "".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which AND gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0):
super().__init__(a, b, prefix)
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0, parent_component: object = None):
super().__init__(a, b, prefix, outid, parent_component)
self.gate_type = "and_gate"
self.cgp_function = 2
self.operator = "&"
self.out = Wire(name=prefix+"_y"+str(outid))
# Logic gate output wire generation based on input values
# If constant input is present, logic gate is not generated and corresponding
# input value is propagated to the output to connect to other components
if a.is_const() and a.value == 1:
self.out = b
self.disable_generation = True
elif a.is_const() and a.value == 0:
self.out = ConstantWireValue0()
self.disable_generation = True
elif b.is_const() and b.value == 1:
self.out = a
self.disable_generation = True
elif b.is_const() and b.value == 0:
self.out = ConstantWireValue0()
self.disable_generation = True
else:
self.out = Wire(name=prefix)
""" BLIF CODE GENERATION """
def get_function_blif_flat(self):
def get_function_blif(self):
"""Generates Blif code representing AND gate Boolean function using its truth table.
Returns:
str: Blif description of AND gate's Boolean function.
"""
return f".names {self.a.name} {self.b.name} {self.out.name}\n" + \
f"11 1\n"
if self.disable_generation:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.name}_out\n" + \
f"11 1\n"
else:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.get_wire_value_blif()}\n" + \
f"11 1\n"
class NandGate(InvertedLogicGate):
"""Class representing two input nand gate.
class NandGate(TwoInputInvertedLogicGate):
"""Class representing two input NAND gate.
```
@ -415,29 +78,55 @@ class NandGate(InvertedLogicGate):
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "".
prefix (str, optional): Prefix name of NAND gate. Defaults to "".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which NAND gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0):
super().__init__(a, b, prefix)
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0, parent_component: object = None):
super().__init__(a, b, prefix, outid, parent_component)
self.gate_type = "nand_gate"
self.cgp_function = 5
self.operator = "&"
self.out = Wire(name=prefix+"_y"+str(outid))
# Logic gate output wire generation based on input values
# If constant input is present, logic gate is not generated and corresponding
# input value is propagated to the output to connect to other components
if a.is_const() and a.value == 1:
output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component)
self.parent_component.add_component(output) if parent_component is not None else None
self.out = output.out
self.disable_generation = True
elif a.is_const() and a.value == 0:
self.out = ConstantWireValue1()
self.disable_generation = True
elif b.is_const() and b.value == 1:
output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component)
self.parent_component.add_component(output) if parent_component is not None else None
self.out = output.out
self.disable_generation = True
elif b.is_const() and b.value == 0:
self.out = ConstantWireValue1()
self.disable_generation = True
else:
self.out = Wire(name=prefix)
""" BLIF CODE GENERATION """
def get_function_blif_flat(self):
def get_function_blif(self):
"""Generates Blif code representing NAND gate Boolean function using its truth table.
Returns:
str: Blif description of NAND gate's Boolean function.
"""
return f".names {self.a.name} {self.b.name} {self.out.name}\n" + \
f"0- 1\n-0 1\n"
if self.disable_generation:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.name}_out\n" + \
f"0- 1\n-0 1\n"
else:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.get_wire_value_blif()}\n" + \
f"0- 1\n-0 1\n"
class OrGate(LogicGate):
"""Class representing two input or gate.
class OrGate(TwoInputLogicGate):
"""Class representing two input OR gate.
```
@ -452,29 +141,51 @@ class OrGate(LogicGate):
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "".
prefix (str, optional): Prefix name of OR gate. Defaults to "".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which OR gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0):
super().__init__(a, b, prefix)
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0, parent_component: object = None):
super().__init__(a, b, prefix, outid, parent_component)
self.gate_type = "or_gate"
self.cgp_function = 3
self.operator = "|"
self.out = Wire(name=prefix+"_y"+str(outid))
# Logic gate output wire generation based on input values
# If constant input is present, logic gate is not generated and corresponding
# input value is propagated to the output to connect to other components
if a.is_const() and a.value == 1:
self.out = ConstantWireValue1()
self.disable_generation = True
elif a.is_const() and a.value == 0:
self.out = b
self.disable_generation = True
elif b.is_const() and b.value == 1:
self.out = ConstantWireValue1()
self.disable_generation = True
elif b.is_const() and b.value == 0:
self.out = a
self.disable_generation = True
else:
self.out = Wire(name=prefix)
""" BLIF CODE GENERATION """
def get_function_blif_flat(self):
def get_function_blif(self):
"""Generates Blif code representing OR gate Boolean function using its truth table.
Returns:
str: Blif description of OR gate's Boolean function.
"""
return f".names {self.a.name} {self.b.name} {self.out.name}\n" + \
f"1- 1\n-1 1\n"
if self.disable_generation:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.name}_out\n" + \
f"1- 1\n-1 1\n"
else:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.get_wire_value_blif()}\n" + \
f"1- 1\n-1 1\n"
class NorGate(InvertedLogicGate):
"""Class representing two input nor gate.
class NorGate(TwoInputInvertedLogicGate):
"""Class representing two input NOR gate.
```
@ -489,29 +200,55 @@ class NorGate(InvertedLogicGate):
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "".
prefix (str, optional): Prefix name of NOR gate. Defaults to "".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which NOR gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0):
super().__init__(a, b, prefix)
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0, parent_component: object = None):
super().__init__(a, b, prefix, outid, parent_component)
self.gate_type = "nor_gate"
self.cgp_function = 6
self.operator = "|"
self.out = Wire(name=prefix+"_y"+str(outid))
# Logic gate output wire generation based on input values
# If constant input is present, logic gate is not generated and corresponding
# input value is propagated to the output to connect to other components
if a.is_const() and a.value == 1:
self.out = ConstantWireValue0()
self.disable_generation = True
elif a.is_const() and a.value == 0:
output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component)
self.parent_component.add_component(output) if parent_component is not None else None
self.out = output.out
self.disable_generation = True
elif b.is_const() and b.value == 1:
self.out = ConstantWireValue0()
self.disable_generation = True
elif b.is_const() and b.value == 0:
output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component)
self.parent_component.add_component(output) if parent_component is not None else None
self.out = output.out
self.disable_generation = True
else:
self.out = Wire(name=prefix)
""" BLIF CODE GENERATION """
def get_function_blif_flat(self):
def get_function_blif(self):
"""Generates Blif code representing NOR gate Boolean function using its truth table.
Returns:
str: Blif description of NOR gate's Boolean function.
"""
return f".names {self.a.name} {self.b.name} {self.out.name}\n" + \
f"00 1\n"
if self.disable_generation:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.name}_out\n" + \
f"00 1\n"
else:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.get_wire_value_blif()}\n" + \
f"00 1\n"
class XorGate(LogicGate):
"""Class representing two input xor gate.
class XorGate(TwoInputLogicGate):
"""Class representing two input XOR gate.
```
@ -526,29 +263,55 @@ class XorGate(LogicGate):
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "".
prefix (str, optional): Prefix name of XOR gate. Defaults to "".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which XOR gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0):
super().__init__(a, b, prefix)
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0, parent_component: object = None):
super().__init__(a, b, prefix, outid, parent_component)
self.gate_type = "xor_gate"
self.cgp_function = 4
self.operator = "^"
self.out = Wire(name=prefix+"_y"+str(outid))
# Logic gate output wire generation based on input values
# If constant input is present, logic gate is not generated and corresponding
# input value is propagated to the output to connect to other components
if a.is_const() and a.value == 1:
output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component)
self.parent_component.add_component(output) if parent_component is not None else None
self.out = output.out
self.disable_generation = True
elif a.is_const() and a.value == 0:
self.out = b
self.disable_generation = True
elif b.is_const() and b.value == 1:
output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component)
self.parent_component.add_component(output) if parent_component is not None else None
self.out = output.out
self.disable_generation = True
elif b.is_const() and b.value == 0:
self.out = a
self.disable_generation = True
else:
self.out = Wire(name=prefix)
""" BLIF CODE GENERATION """
def get_function_blif_flat(self):
def get_function_blif(self):
"""Generates Blif code representing XOR gate Boolean function using its truth table.
Returns:
str: Blif description of XOR gate's Boolean function.
"""
return f".names {self.a.name} {self.b.name} {self.out.name}\n" + \
f"01 1\n10 1\n"
if self.disable_generation:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.name}_out\n" + \
f"01 1\n10 1\n"
else:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.get_wire_value_blif()}\n" + \
f"01 1\n10 1\n"
class XnorGate(InvertedLogicGate):
"""Class representing two input xnor gate.
class XnorGate(TwoInputInvertedLogicGate):
"""Class representing two input XNOR gate.
```
@ -563,30 +326,56 @@ class XnorGate(InvertedLogicGate):
Args:
a (Wire): First input wire.
b (Wire): Second input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "".
prefix (str, optional): Prefix name of XNOR gate. Defaults to "".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which XNOR gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0):
super().__init__(a, b, prefix)
def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0, parent_component: object = None):
super().__init__(a, b, prefix, outid, parent_component)
self.gate_type = "xnor_gate"
self.cgp_function = 7
self.operator = "^"
self.out = Wire(name=prefix+"_y"+str(outid))
# Logic gate output wire generation based on input values
# If constant input is present, logic gate is not generated and corresponding
# input value is propagated to the output to connect to other components
if a.is_const() and a.value == 1:
self.out = b
self.disable_generation = True
elif a.is_const() and a.value == 0:
output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component)
self.parent_component.add_component(output) if parent_component is not None else None
self.out = output.out
self.disable_generation = True
elif b.is_const() and b.value == 1:
self.out = a
self.disable_generation = True
elif b.is_const() and b.value == 0:
output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component)
self.parent_component.add_component(output) if parent_component is not None else None
self.out = output.out
self.disable_generation = True
else:
self.out = Wire(name=prefix)
""" BLIF CODE GENERATION """
def get_function_blif_flat(self):
def get_function_blif(self):
"""Generates Blif code representing XNOR gate Boolean function using its truth table.
Returns:
str: Blif description of XNOR gate's Boolean function.
"""
return f".names {self.a.name} {self.b.name} {self.out.name}\n" + \
f"00 1\n11 1\n"
if self.disable_generation:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.name}_out\n" + \
f"00 1\n11 1\n"
else:
return f".names {self.a.get_wire_value_blif()} {self.b.get_wire_value_blif()} {self.out.get_wire_value_blif()}\n" + \
f"00 1\n11 1\n"
# Single-input #
class NotGate(InvertedLogicGate):
"""Class representing one input not gate.
class NotGate(OneInputLogicGate):
"""Class representing one input NOT gate.
```
@ -600,190 +389,38 @@ class NotGate(InvertedLogicGate):
Args:
a (Wire): Input wire.
prefix (str, optional): Prefix name of logic gate. Defaults to "".
prefix (str, optional): Prefix name of NOT gate. Defaults to "".
outid (int, optional): Index of output wire. Defaults to 0.
parent_component (object, optional) Object of upper component of which NOT gate is a subcomponent. Defaults to None.
"""
def __init__(self, a: Wire, prefix: str = "", outid: int = 0):
def __init__(self, a: Wire, prefix: str = "", outid: int = 0, parent_component: object = None):
super().__init__(a, prefix, outid, parent_component)
self.gate_type = "not_gate"
self.cgp_function = 1
self.operator = "~"
self.a = Wire(name=prefix+"_"+a.name.replace(prefix+"_", ""), value=a.value)
self.prefix = prefix
self.out = Wire(name=prefix+"_y"+str(outid))
""" C CODE GENERATION """
# FLAT C #
def get_prototype_c(self):
"""Generates C code function header to describe corresponding one input logic gate's interface in C code.
Returns:
str: Function's name and parameter in C code.
"""
return f"uint8_t {self.gate_type}(uint8_t {self.a.name})" + "{" + "\n"
def get_function_c(self):
"""Generates C code representing corresponding one input logic gate's Boolean function using bitwise operators between its bitwise shifted input.
Returns:
str: C code description of logic gate's Boolean function (with bitwise shifted input).
"""
return f"{self.operator}{self.a.get_wire_value_c()} & 0x01 << 0"
def get_declaration_c_flat(self):
"""Generates C code declaration of input/output wires.
Returns:
str: C code logic gate's wires declaration.
"""
return f"{self.a.get_declaration_c()}{self.out.get_declaration_c()}"
def get_init_c_flat(self):
"""Generates C code representing corresponding one input logic gate's Boolean function using bitwise operators between its input.
Returns:
str: C code description of logic gate's Boolean function.
"""
return f"{self.operator}{self.a.name}"
def get_assign_c_flat(self):
"""Generates C code for assigning initial values into logic gate's wires.
Returns:
str: C code initialization of wires values.
"""
return f"{self.a.get_assign_c(name=self.a.name.replace(self.prefix+'_', ''))}" + \
f" {self.out.prefix} = {self.get_init_c_flat()};\n"
# HIERARCHICAL C #
def get_gate_invocation_c(self, remove_prefix: bool = True):
"""Generates C code invocation of corresponding logic gate's generated function block.
Args:
remove_prefix (bool, optional): Specifies whether function block's input parameter name should contain also its prefix (parent circuit name) or not. Defaults to True.
Returns:
str: C code logic gate's function block invocation.
"""
a_name = self.a.name.replace(self.prefix+"_", "") if remove_prefix is True else self.a.name
return f"{self.gate_type}({a_name});"
""" VERILOG CODE GENERATION """
# FLAT VERILOG #
def get_prototype_v(self):
"""Generates Verilog module header to describe corresponding one input logic gate's interface in Verilog.
Returns:
str: Module's name and parameter in Verilog.
"""
return f"module {self.gate_type}(input {self.a.name}, output {self.out.name});\n"
def get_declaration_v_flat(self):
"""Generates Verilog code declaration of input/output wires.
Returns:
str: Verilog code logic gate's wires declaration.
"""
return f"{self.a.get_declaration_v()}{self.out.get_declaration_v()}"
def get_init_v_flat(self):
"""Generates Verilog code representing corresponding one input logic gate's Boolean function using bitwise operators between its input.
Returns:
str: Verilog description of logic gate's Boolean function.
"""
return f"{self.operator}{self.a.name}"
def get_assign_v_flat(self):
"""Generates Verilog code for assigning initial values into logic gate's wires.
Returns:
str: Verilog code initialization of wires values.
"""
return f"{self.a.get_assign_v(name=self.a.name.replace(self.prefix+'_', ''))}" + \
f" assign {self.out.prefix} = {self.get_init_v_flat()};\n"
# HIERARCHICAL VERILOG #
def get_gate_invocation_v(self, remove_prefix: bool = True):
"""Generates Verilog code invocation of corresponding logic gate's generated function block.
Args:
remove_prefix (bool, optional): Specifies whether function block's input parameter name should contain also its prefix (parent circuit name) or not. Defaults to True.
Returns:
str: Verilog code logic gate's function block invocation.
"""
a_name = self.a.name.replace(self.prefix+"_", "") if remove_prefix is True else self.a.name
return f" {self.gate_type} {self.gate_type}_{self.out.name}({a_name}, {self.out.name});\n"
# Logic gate output wire generation based on input values
# If constant input is present, logic gate is not generated and corresponding
# input value is propagated to the output to connect to other components
if a.is_const() and a.value == 1:
self.out = ConstantWireValue0()
self.disable_generation = True
elif a.is_const() and a.value == 0:
self.out = ConstantWireValue1()
self.disable_generation = True
else:
self.out = Wire(name=prefix)
""" BLIF CODE GENERATION """
# FLAT BLIF #
def get_declaration_blif(self):
"""Generates Blif model header to describe corresponding logic gate's interface in Blif.
Returns:
str: Model's name and parameter in Blif.
"""
return f".inputs {self.a.name}\n" + \
f".outputs {self.out.name}\n"
def get_function_blif_flat(self):
def get_function_blif(self):
"""Generates Blif code representing NOT gate Boolean function using its truth table.
Returns:
str: Blif description of NOT gate's Boolean function.
"""
return f".names {self.a.name} {self.out.name}\n" + \
f"0 1\n"
def get_init_function_blif_flat(self):
"""Generates Blif code representing corresponding one input logic gate's Boolean function between its input.
Returns:
str: Blif description of logic gate's Boolean function.
"""
return f"{self.a.get_assign_blif(name=self.a.name.replace(self.prefix+'_', ''))}" + \
f"{self.get_function_blif_flat()}"
# HIERARCHICAL BLIF #
def get_invocation_blif_hier(self, init: bool = False):
"""Generates Blif code invocation of corresponding logic gate's generated subcomponent.
Args:
init (bool, optional): Specifies whether subcomponent's input wire initializiation is neccessary before its invocation. Defaults to False.
Returns:
str: Blif logic gate subcomponent invocation.
"""
if init is True:
return f"{self.a.get_assign_blif(name=self.a.name.replace(self.prefix+'_', ''))}" + \
f".subckt {self.gate_type} _a={self.a.name} _y0={self.out.name}\n"
if self.disable_generation:
return f".names {self.a.get_wire_value_blif()} {self.out.name}_out\n" + \
f"0 1\n"
else:
return f".subckt {self.gate_type} _a={self.a.name} _y0={self.out.name}\n"
""" CGP CODE GENERATION """
# FLAT CGP #
def get_triplet_cgp(self, a_index: int = 0):
"""Generates logic gate triplet (2 input wires, logic gate function) using wires unique position indexes within the described circuit.
Each triplet represents unique logic gate within the described circuit. In this case of one input logic gate, the same input wire index is driven to both inputs.
Besides the contained input wires indexes and gate's inner logic function, an output wire with incremented index position is also created and remembered to be
appropriately driven as an input to another logic gate or as the circuit's output.
Args:
a_index (int, optional): First (used also as the second) input wire index position. Defaults to 0.
Returns:
str: Triplet describing function of corresponding one input logic gate.
"""
return f"({a_index},{a_index},{self.cgp_function})"
@staticmethod
def get_output_cgp(out_index: int = 1):
"""Generates list of output wires indexes of described one input logic gate from MSB to LSB.
Args:
out_index (int, optional): Output wire index. Defaults to 1.
Returns:
str: List of logic gate's output wire indexes.
"""
return f"({out_index})"
return f".names {self.a.get_wire_value_blif()} {self.out.get_wire_value_blif()}\n" + \
f"0 1\n"

View File

@ -1,12 +1,11 @@
from typing import FrozenSet
from .two_input_one_bit_components import (
HalfAdder,
PGLogicBlock,
ConstantWireValue0,
ConstantWireValue1
)
from .three_input_one_bit_components import(
FullAdder,
FullAdderPG
FullAdderPG,
TwoOneMultiplexer,
FullSubtractor
)

View File

@ -1,5 +1,6 @@
from ariths_gen.core import ThreeInputOneBitCircuit
from ariths_gen.one_bit_circuits.logic_gates import LogicGate, AndGate, NandGate, OrGate, NorGate, XorGate, XnorGate, NotGate
from ariths_gen.wire_components.wires import ConstantWireValue0
from ariths_gen.core.one_bit_circuits import ThreeInputOneBitCircuit
from ariths_gen.one_bit_circuits.logic_gates import AndGate, NandGate, OrGate, NorGate, XorGate, XnorGate, NotGate
from ariths_gen.wire_components import Wire, Bus
@ -7,11 +8,11 @@ class FullAdder(ThreeInputOneBitCircuit):
"""Class representing three input one bit full adder.
```
Sum
Cout
Sum
Cout
```
Description of the __init__ method.
@ -23,33 +24,28 @@ class FullAdder(ThreeInputOneBitCircuit):
prefix (str, optional): Prefix name of full adder. Defaults to "fa".
"""
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "fa"):
super().__init__()
self.c_data_type = "uint8_t"
self.prefix = prefix
self.a = a
self.b = b
self.c = c
super().__init__(a, b, c, prefix)
# 2 wires for component's bus output (sum, cout)
self.out = Bus("out", self.N+1)
self.out = Bus(self.prefix+"_out", 2)
# PG logic
propagate_xor = XorGate(a, b, prefix=self.prefix, outid=0)
propagate_xor = XorGate(a, b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(propagate_xor)
generate_and = AndGate(a, b, prefix=self.prefix, outid=1)
generate_and = AndGate(a, b, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), parent_component=self)
self.add_component(generate_and)
# Sum
# XOR gate for calculation of 1-bit sum
obj_xor = XorGate(propagate_xor.out, c, prefix=self.prefix, outid=2)
obj_xor = XorGate(propagate_xor.out, c, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self)
self.add_component(obj_xor)
self.out.connect(0, obj_xor.out)
# Cout
# AND gate for calculation of 1-bit cout
obj_and = AndGate(propagate_xor.out, c, prefix=self.prefix, outid=3)
obj_and = AndGate(propagate_xor.out, c, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), parent_component=self)
self.add_component(obj_and)
obj_or = OrGate(generate_and.out, obj_and.out, prefix=self.prefix, outid=4)
obj_or = OrGate(generate_and.out, obj_and.out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate)), outid=1, parent_component=self)
self.add_component(obj_or)
self.out.connect(1, obj_or.out)
@ -59,11 +55,11 @@ class FullAdderPG(ThreeInputOneBitCircuit):
"""Class representing modified three input one bit full adder with propagate/generate logic.
```
P
G
Sum
P
G
Sum
```
Description of the __init__ method.
@ -72,29 +68,24 @@ class FullAdderPG(ThreeInputOneBitCircuit):
a (Wire, optional): First input wire. Defaults to Wire(name="a").
b (Wire, optional): Second input wire. Defaults to Wire(name="b").
c (Wire, optional): Carry input wire. Defaults to Wire(name="cin").
prefix (str, optional): Prefix name of full adder. Defaults to "fa_cla".
prefix (str, optional): Prefix name of full adder with pg logic. Defaults to "pg_fa".
"""
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "fa_cla"):
super().__init__()
self.c_data_type = "uint8_t"
self.prefix = prefix
self.a = a
self.b = b
self.c = c
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "pg_fa"):
super().__init__(a, b, c, prefix)
# 3 wires for component's bus output (sum, propagate, generate)
self.out = Bus("out", self.N+2)
self.out = Bus(self.prefix+"_out", 3)
# PG logic
propagate_xor = XorGate(a, b, prefix=self.prefix, outid=0)
propagate_xor = XorGate(a, b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self)
self.add_component(propagate_xor)
self.out.connect(0, propagate_xor.out)
generate_and = AndGate(a, b, prefix=self.prefix, outid=1)
generate_and = AndGate(a, b, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), outid=1, parent_component=self)
self.add_component(generate_and)
self.out.connect(1, generate_and.out)
# Sum output
sum_xor = XorGate(propagate_xor.out, c, prefix=self.prefix, outid=2)
sum_xor = XorGate(propagate_xor.out, c, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), outid=2, parent_component=self)
self.add_component(sum_xor)
self.out.connect(2, sum_xor.out)
@ -121,3 +112,113 @@ class FullAdderPG(ThreeInputOneBitCircuit):
Wire: Return sum wire.
"""
return self.out.get_wire(2)
class TwoOneMultiplexer(ThreeInputOneBitCircuit):
"""Class representing two to one multiplexer (with select signal as its third input).
```
Sel
```
Description of the __init__ method.
Args:
a (Wire, optional): First input wire. Defaults to Wire(name="d0").
b (Wire, optional): Second input wire. Defaults to Wire(name="d1").
c (Wire, optional): Select signal. Defaults to Wire(name="sel").
prefix (str, optional): Prefix name of two to one multiplexer. Defaults to "mux2to1".
"""
def __init__(self, a: Wire = Wire(name="d0"), b: Wire = Wire(name="d1"), c: Wire = Wire(name="sel"), prefix: str = "mux2to1"):
super().__init__(a, b, c, prefix)
# Represents select signal (self.c naming for proper unified generation)
self.c = c
# 1 wire for component's output bus
self.out = Bus(self.prefix+"_out", 1)
# 2:1MUX logic
and_obj = AndGate(a=self.b, b=self.c, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), parent_component=self)
self.add_component(and_obj)
not_obj = NotGate(a=self.c, prefix=self.prefix+"_not"+str(self.get_instance_num(cls=NotGate)), parent_component=self)
self.add_component(not_obj)
and_obj = AndGate(a=self.a, b=self.get_previous_component().out, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), parent_component=self)
self.add_component(and_obj)
xor_obj = XorGate(a=self.get_previous_component(3).out, b=self.get_previous_component().out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(xor_obj)
# Connection of MUX output wire
self.out.connect(0, xor_obj.out)
class FullSubtractor(ThreeInputOneBitCircuit):
"""Class representing three input one bit full subtractor.
```
Difference
Bout
```
Description of the __init__ method.
Args:
a (Wire, optional): First input wire. Defaults to Wire(name="a").
b (Wire, optional): Second input wire. Defaults to Wire(name="b").
c (Wire, optional): Input borrow wire. Defaults to Wire(name="bin").
prefix (str, optional): Prefix name of full subtractor. Defaults to "fs".
"""
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="bin"), prefix: str = "fs"):
super().__init__(a, b, c, prefix)
# 2 wires for component's bus output (difference, bout)
self.out = Bus(self.prefix+"_out", 2)
# Difference
xor_obj = XorGate(a=self.a, b=self.b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(xor_obj)
not_obj = NotGate(a=self.a, prefix=self.prefix+"_not"+str(self.get_instance_num(cls=NotGate)), parent_component=self)
self.add_component(not_obj)
and_obj = AndGate(a=not_obj.out, b=self.b, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), parent_component=self)
self.add_component(and_obj)
difference_xor = XorGate(a=self.c, b=xor_obj.out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self)
self.add_component(difference_xor)
self.out.connect(0, difference_xor.out)
# Borrow out
not_obj = NotGate(a=xor_obj.out, prefix=self.prefix+"_not"+str(self.get_instance_num(cls=NotGate)), outid=1, parent_component=self)
self.add_component(not_obj)
and_obj = AndGate(a=not_obj.out, b=self.c, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), outid=1, parent_component=self)
self.add_component(and_obj)
borrow_out_or = OrGate(a=and_obj.out, b=self.get_previous_component(4).out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate)), outid=1, parent_component=self)
self.add_component(borrow_out_or)
self.out.connect(1, borrow_out_or.out)
def get_difference_wire(self):
"""Get output wire carrying difference value.
Returns:
Wire: Return difference wire.
"""
return self.out.get_wire(0)
def get_borrow_wire(self):
"""Get output wire carrying borrow out value.
Returns:
Wire: Return borrow out wire.
"""
return self.out.get_wire(1)

View File

@ -1,5 +1,5 @@
from ariths_gen.core import TwoInputOneBitCircuit
from ariths_gen.one_bit_circuits.logic_gates import LogicGate, AndGate, NandGate, OrGate, NorGate, XorGate, XnorGate, NotGate
from ariths_gen.core.one_bit_circuits import TwoInputOneBitCircuit
from ariths_gen.one_bit_circuits.logic_gates import AndGate, NandGate, OrGate, NorGate, XorGate, XnorGate, NotGate
from ariths_gen.wire_components import Wire, Bus
@ -7,11 +7,11 @@ class HalfAdder(TwoInputOneBitCircuit):
"""Class representing two input one bit half adder.
```
Sum
Cout
Sum
Cout
```
Description of the __init__ method.
@ -19,26 +19,22 @@ class HalfAdder(TwoInputOneBitCircuit):
Args:
a (Wire, optional): First input wire. Defaults to Wire(name="a").
b (Wire, optional): Second input wire. Defaults to Wire(name="b").
prefix (str, optional): Prefix name of full adder. Defaults to "ha".
prefix (str, optional): Prefix name of half adder. Defaults to "ha".
"""
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "ha"):
super().__init__()
self.c_data_type = "uint8_t"
self.prefix = prefix
self.a = a
self.b = b
super().__init__(a, b, prefix)
# 2 wires for component's bus output (sum, cout)
self.out = Bus("out", self.N+1)
self.out = Bus(self.prefix+"_out", 2)
# Sum
# XOR gate for calculation of 1-bit sum
obj_xor = XorGate(a, b, prefix=self.prefix, outid=0)
obj_xor = XorGate(a, b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self)
self.add_component(obj_xor)
self.out.connect(0, obj_xor.out)
# Cout
# AND gate for calculation of 1-bit cout
obj_and = AndGate(a, b, prefix=self.prefix, outid=1)
obj_and = AndGate(a, b, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), outid=1, parent_component=self)
self.add_component(obj_and)
self.out.connect(1, obj_and.out)
@ -47,11 +43,11 @@ class PGLogicBlock(TwoInputOneBitCircuit):
"""Class representing two input one bit propagate/generate logic block.
```
P
G
S
P
G
S
```
Description of the __init__ method.
@ -59,23 +55,19 @@ class PGLogicBlock(TwoInputOneBitCircuit):
Args:
a (Wire, optional): First input wire. Defaults to Wire(name="a").
b (Wire, optional): Second input wire. Defaults to Wire(name="b").
prefix (str, optional): Prefix name of full adder. Defaults to "pg_logic".
prefix (str, optional): Prefix name of pg logic block. Defaults to "pg_logic".
"""
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "pg_logic"):
super().__init__()
self.c_data_type = "uint8_t"
self.prefix = prefix
self.a = a
self.b = b
super().__init__(a, b, prefix)
# 3 wires for component's bus output (propagate, generate, sum)
self.out = Bus("out", self.N+2)
self.out = Bus(self.prefix+"_out", 3)
# PG logic
propagate_or = OrGate(a, b, prefix=self.prefix, outid=0)
propagate_or = OrGate(a, b, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate)), outid=0, parent_component=self)
self.add_component(propagate_or)
generate_and = AndGate(a, b, prefix=self.prefix, outid=1)
generate_and = AndGate(a, b, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=AndGate)), outid=1, parent_component=self)
self.add_component(generate_and)
sum_xor = XorGate(a, b, prefix=self.prefix, outid=2)
sum_xor = XorGate(a, b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), outid=2, parent_component=self)
self.add_component(sum_xor)
self.out.connect(0, propagate_or.out)
@ -107,15 +99,15 @@ class PGLogicBlock(TwoInputOneBitCircuit):
return self.out.get_wire(2)
class ConstantWireValue0(TwoInputOneBitCircuit):
"""Class representing two input one bit constant wire with value 0 generation block.
class HalfSubtractor(TwoInputOneBitCircuit):
"""Class representing two input one bit half subtractor.
```
0
Difference
Bout
```
Description of the __init__ method.
@ -123,68 +115,40 @@ class ConstantWireValue0(TwoInputOneBitCircuit):
Args:
a (Wire, optional): First input wire. Defaults to Wire(name="a").
b (Wire, optional): Second input wire. Defaults to Wire(name="b").
prefix (str, optional): Prefix name of full adder. Defaults to "constant_wire_value_0".
prefix (str, optional): Prefix name of half subtractor adder. Defaults to "hs".
"""
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "constant_wire_value_0"):
super().__init__()
self.c_data_type = "uint8_t"
self.prefix = prefix
self.a = a
self.b = b
# 1 wire for component's bus output (constant wire)
self.out = Bus("out", self.N)
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "hs"):
super().__init__(a, b, prefix)
# 2 wires for component's bus output (difference, bout)
self.out = Bus(self.prefix+"_out", 2)
# Generation of wire with constant value 0
obj_xor = XorGate(self.a, self.b, prefix=self.prefix, outid=0)
obj_xnor = XnorGate(self.a, self.b, prefix=self.prefix, outid=1)
obj_nor = NorGate(obj_xor.out, obj_xnor.out, prefix=self.prefix, outid=2)
obj_nor.out.name = "constant_wire_0"
obj_nor.out.prefix = "constant_wire_0"
# Difference
# XOR gate for calculation of 1-bit difference
difference_xor = XorGate(a=self.a, b=self.b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self)
self.add_component(difference_xor)
self.out.connect(0, difference_xor.out)
self.add_component(obj_xor)
self.add_component(obj_xnor)
self.add_component(obj_nor)
# Bout
# NOT and AND gates for calculation of 1-bit borrow out
not_obj = NotGate(a=self.a, prefix=self.prefix+"_not"+str(self.get_instance_num(cls=NotGate)), parent_component=self)
self.add_component(not_obj)
# Constant wire output
self.out.connect(0, obj_nor.out)
borrow_and = AndGate(a=not_obj.out, b=self.b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), outid=1, parent_component=self)
self.add_component(borrow_and)
self.out.connect(1, borrow_and.out)
def get_difference_wire(self):
"""Get output wire carrying difference value.
class ConstantWireValue1(TwoInputOneBitCircuit):
"""Class representing two input one bit constant wire with value 1 generation block.
Returns:
Wire: Return difference wire.
"""
return self.out.get_wire(0)
```
1
```
def get_borrow_wire(self):
"""Get output wire carrying borrow out value.
Description of the __init__ method.
Args:
a (Wire, optional): First input wire. Defaults to Wire(name="a").
b (Wire, optional): Second input wire. Defaults to Wire(name="b").
prefix (str, optional): Prefix name of full adder. Defaults to "constant_wire_value_1".
"""
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "constant_wire_value_1"):
super().__init__()
self.c_data_type = "uint8_t"
self.prefix = prefix
self.a = a
self.b = b
# 1 wire for component's bus output (constant wire)
self.out = Bus("out", self.N)
# Generation of wire with constant value 1
obj_xor = XorGate(self.a, self.b, prefix=self.prefix, outid=0)
obj_xnor = XnorGate(self.a, self.b, prefix=self.prefix, outid=1)
obj_or = OrGate(obj_xor.out, obj_xnor.out, prefix=self.prefix, outid=2)
obj_or.out.name = "constant_wire_1"
obj_or.out.prefix = "constant_wire_1"
self.add_component(obj_xor)
self.add_component(obj_xnor)
self.add_component(obj_or)
# Constant wire output
self.out.connect(0, obj_or.out)
Returns:
Wire: Return borrow out wire.
"""
return self.out.get_wire(1)

View File

@ -1,2 +1,2 @@
from .wires import Wire
from .wires import Wire, ConstantWireValue0, ConstantWireValue1
from .buses import Bus

View File

@ -13,36 +13,15 @@ class Bus():
"""
def __init__(self, prefix: str = "bus", N: int = 1, wires_list: list = None):
if wires_list is None:
self.bus = [Wire(name=prefix+"_"+str(i), index=i) for i in range(N)]
self.prefix = prefix
# Adding wires into current bus's wires list (wire names are concatenated from bus prefix and their index position inside the bus in square brackets)
self.bus = [Wire(name=prefix+f"[{i}]", prefix=prefix, index=i, parent_bus=self) for i in range(N)]
self.N = N
else:
self.bus = wires_list
self.prefix = prefix
self.bus = wires_list
self.N = len(self.bus)
# Connecting output wire of the inner circuit component to the input of another component
# (or to the wire of the circuit's output bus)
def connect(self, bus_wire_index: int, inner_component_out_wire: Wire):
"""Connects given 'Wire' object to a 'bus_wire_index' within this bus.
Args:
bus_wire_index (int): Index in bus to store given wire in.
inner_component_out_wire (Wire): Wire of some other component (mostly its output) to store in the bus.
"""
self.bus[bus_wire_index] = inner_component_out_wire
def get_wire(self, wire_index: int = 0):
"""Retrieves a wire from the bus by a given index.
Args:
wire_index (int, optional): Index of wire to retrieve from the bus. Defaults to 0.
Returns:
Wire: Return wire from the bus.
"""
return self.bus[wire_index]
def bus_extend(self, N: int, prefix: str = "bus"):
"""Provides bus extension to contain more wires.
@ -50,10 +29,46 @@ class Bus():
N (int): Number of wires in the bus. Defaults to 1.
prefix (str, optional): Prefix name of the bus. Defaults to "bus".
"""
# Checks if any extension is neccesarry and if so, proceeds to wire extend the bus
if self.N < N:
self.bus += [Wire(name=prefix+"_"+str(i), index=i) for i in range(self.N, N)]
# Adding wires into current bus's wires list (wire names are concatenated from bus prefix and their index position inside the bus in square brackets)
self.bus += [Wire(name=prefix+f"[{i}]", prefix=prefix, index=i, parent_bus=self) for i in range(self.N, N)]
self.N = N
def get_wire(self, wire_index: int = 0):
"""Retrieves a wire from the bus by a given index.
Args:
wire_index (int, optional): Index of wire to be retrieved from the bus. Defaults to 0.
Returns:
Wire: Returning wire from the bus.
"""
return self.bus[wire_index]
# Connecting output wire of the inner circuit component to desired position in the described circuit's output bus
def connect(self, bus_wire_index: int, inner_component_out_wire: Wire, inserted_wire_desired_index: int = -1):
"""Connects given 'Wire' object to a 'bus_wire_index' within this bus.
Used for connection of output wire of the inner circuit component
to the appropriate wire of the circuit's output bus.
Args:
bus_wire_index (int): Index in bus to store given wire in.
inner_component_out_wire (Wire): Wire of some other component (mostly its output) to store in the bus.
inserted_wire_desired_index(int, optional): Optional desired explicit index, where 'inner_component_out_wire' value resides in the inner components's output bus. Otherwise 'inner_component_out_wire' self index value is used. Defaults to -1.
"""
inserted_wire_index = inserted_wire_desired_index if inserted_wire_desired_index != -1 else inner_component_out_wire.index
# Used for connection of constant wire value into a bus
if inner_component_out_wire.is_const():
self.bus[bus_wire_index] = inner_component_out_wire
# Proper connection of wires that themselves are not yet a member of any other bus and also those that could be part of some bus but do not have `inserted_wire_desired_index` defined
elif inner_component_out_wire.parent_bus is None or inserted_wire_desired_index == -1:
self.bus[bus_wire_index] = Wire(name=inner_component_out_wire.name, prefix=inner_component_out_wire.prefix, index=inserted_wire_index, value=inner_component_out_wire.value, parent_bus=self)
# Proper connection of wires that are already a member of some other bus and are desired to connect value from their previous bus to this one at desired index position
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)
""" C CODE GENERATION """
def get_declaration_c(self):
"""Bus declaration in C code.
@ -66,76 +81,81 @@ class Bus():
else:
return f" uint8_t {self.prefix} = 0;\n"
def get_wire_declaration_c(self):
"""Declare each wire from the bus independently.
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.
Returns:
str: C code for declaration and initialization of bus wires.
str: C code for assigning wire values into bus represented in C code variable.
"""
return "".join([w.get_declaration_c() for w in self.bus])
return "".join([f" {self.prefix} |= {w.return_wire_value_c_flat(offset=self.bus.index(w))}" for w in self.bus])
def get_wire_assign_c(self, bus_prefix: str = ""):
"""Assign all bits from the bus to each individual wires in C code.
Args:
bus_prefix (str, optional): Custom bus prefix to use for assignment. Defaults to "".
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.
Returns:
str: C code for bus wires assignments.
str: C code for assigning wire values into bus represented in C code variable.
"""
bus_prefix = self.prefix if bus_prefix == "" else bus_prefix
return "".join([w.get_assign_c(name=w.get_wire_value_c(name=bus_prefix, offset=self.bus.index(w))) for w in self.bus])
def return_wire_value_c(self, offset: int = 0):
"""Retrieve wire value from desired index position in the bus.
Args:
offset (int, optional): Offset position of the wire to be retrieved. Defaults to 0.
"""
self.get_wire(wire_index=offset).return_wire_value_c(offset=offset)
return "".join([f" {self.prefix} |= {w.return_wire_value_c_hier(offset=self.bus.index(w))}" for w in self.bus])
""" VERILOG CODE GENERATION """
def get_wire_declaration_v(self):
"""Declare each wire from the bus independently.
def return_bus_wires_values_v_flat(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 flat generation.
Returns:
str: Verilog code for declaration of bus wires.
str: Verilog code for assigning wire values into bus represented in Verilog code bus variable.
"""
return "".join([w.get_declaration_v() for w in self.bus])
return "".join([f" assign {self.prefix}[{self.bus.index(w)}] = {w.return_wire_value_v_flat()}" for w in self.bus])
def get_wire_assign_v(self, bus_prefix: str = ""):
"""Assign all bits from the bus to each individual wires in Verilog code.
Args:
bus_prefix (str, optional): Custom bus prefix to use for assignment. Defaults to "".
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.
Returns:
str: Verilog code for bus wires assignments.
str: Verilog code for assigning wire values into bus represented in Verilog code variable.
"""
bus_prefix = self.prefix if bus_prefix == "" else bus_prefix
return "".join([w.get_assign_v(name=self.prefix, offset=self.bus.index(w), array=True) for w in self.bus])
return "".join([f" assign {self.prefix}[{self.bus.index(w)}] = {w.return_wire_value_v_hier()}" for w in self.bus])
def get_unique_assign_out_wires_v(self):
"""Returns bus's wires used for hierarchical one bit subcomponent's function block invocation and output wires assignments.
Returns:
str: Verilog code unique bus wires for proper subcomponent's function block invocation.
"""
unique_out_wires = []
[unique_out_wires.append(w.prefix) if w.prefix not in unique_out_wires else None for w in self.bus]
return "".join([f", {unique_out_wires.pop(unique_out_wires.index(o.prefix))}" if o.prefix in unique_out_wires else "" for o in self.bus])
""" BLIF CODE GENERATION """
def get_wire_declaration_blif(self, array: bool = True):
"""Declare each wire from the bus independently.
"""Declare each wire from the bus independently in Blif code representation.
Argument `array` specifies whether to declare wires from bus by their offset e.g. out[0]
or by their wire name e.g. out_0.
Args:
array (bool, optional): Determines in which manner bus wire names are declared. Defaults to True.
array (bool, optional): Specifies whether to declare wires from bus by their offset e.g. out[0] or by their wire name e.g. out_0. Defaults to True.
Returns:
str: Blif code for declaration of bus wires.
str: Blif code for declaration of individual bus wires.
"""
return "".join([w.get_declaration_blif(name=self.prefix, offset=self.bus.index(w), array=array) for w in self.bus])
return "".join([f" {w.get_declaration_blif(prefix=self.prefix, offset=self.bus.index(w), array=array)}" for w in self.bus])
def get_wire_assign_blif(self, output: bool = False):
"""Assign all bits from the bus as each individual wires in Blif code.
"""Assign all bits from the bus as each individual wires or assign wires into the corresponding output bus position in Blif code representation.
Args:
output (bool, optional): Specifies whether bus wires are used as outputs (assigned to) or as inputs (assigned from). Defaults to False.
output (bool, optional): Specifies whether bus wires are used as outputs (True, assigned to) or as inputs (False, assigned from). Defaults to False.
Returns:
str: Blif code for bus wires assignments.
"""
return "".join([w.get_assign_blif(name=self.prefix+f"[{self.bus.index(w)}]", output=output) for w in self.bus])
return "".join([w.get_assign_blif(prefix=self.prefix+f"[{self.bus.index(w)}]", output=output) for w in self.bus])
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.
Args:
function_block_out_bus (object): Specifies output bus of corresponding function block's outputs for proper subcomponent modul invocation.
Returns:
str: Blif code for proper subcomponent's function block invocation with respective output wires assignment.
"""
unique_out_wires = []
[unique_out_wires.append(w.prefix) if w.prefix not in unique_out_wires else None for w in self.bus]
return "".join([f" {function_block_out_bus.get_wire(self.bus.index(o)).name}={unique_out_wires.pop(unique_out_wires.index(o.prefix))}" if o.prefix in unique_out_wires else "" for o in self.bus])

View File

@ -5,97 +5,170 @@ class Wire():
Args:
name (str): Name of the wire.
value (int, optional): Value it carries (0,1). Defaults to 0.
prefix (str, optional): Prefix of the wire. Defaultly the same as its name. Defaults to "".
value (int, optional): Value the wire carries (0,1). Defaults to 0.
index (int, optional): Index position of wire (mainly used for indexing within a bus). Defaults to 0.
parent_bus (object, optional): Bus object of which a Wire is a part of (used mainly for proper generation of wire names). Defaults to None.
"""
def __init__(self, name: str, value: int = 0, index: int = 0):
def __init__(self, name: str, prefix: str = "", value: int = 0, index: int = 0, parent_bus: object = None):
self.name = name
if self.name.endswith("_"+str(index)):
self.prefix = name[0:int(name.rfind(str(index))-1)]
else:
self.prefix = name
self.value = value
self.index = index
self.prefix = name if prefix == "" else prefix
self.parent_bus = parent_bus
@staticmethod
def is_const():
"""Information whether wire carries constant value.
Returns:
bool: False, because basic wire doesn't represent a wire with constant value.
"""
return False
""" C CODE GENERATION """
def get_declaration_c(self):
"""Wire declaration in C code.
Returns:
str: C code for declaration and initialization of wire's name.
str: Empty string if C code wire is carrying constant value (constant value is used in situ) or returns C code for declaration and initialization of wire's name.
"""
return f" uint8_t {self.name} = {self.value};\n"
if self.is_const():
return ""
else:
return f" uint8_t {self.name} = {self.value};\n"
def get_wire_value_c(self, name: str = "", offset: int = 0):
"""Access desired bit from wire represented in C code variable.
Args:
name (str, optional): Name representing a wire. Defaults to "".
offset (int, optional): Access desired wire bit from C code variable (used to access wire bits from buses). Defaults to 0.
def get_wire_value_c_flat(self):
"""Accesses desired bit value from wire represented in C code variable used for flat generation.
Returns:
str: C code bitwise shift to get bit from `offset` position in `w_name`.
str: C code bitwise shift to get desired bit value from this wire or wire variable's constant bit value 0/1.
"""
w_name = self.name if name == "" else name
return f"(({w_name} >> {offset}) & 0x01)"
if self.is_const():
return f"({self.c_const})"
else:
# If wire is part of an input bus (where wire names are concatenated from bus prefix and their index position inside the bus in square brackets)
# then the wire value is obtained from bitwise shifting the required wire from the parent bus ('parent_bus.prefix' is the same value as 'self.prefix')
if self.name.endswith("["+str(self.index)+"]") and self.parent_bus is not None:
return f"(({self.prefix} >> {self.index}) & 0x01)"
else:
return f"(({self.name} >> 0) & 0x01)"
def get_assign_c(self, name: str):
"""Assign (connect) the value of wire to another wire.
Args:
name (str): Name of wire to assign value from.
def get_wire_value_c_hier(self):
"""Accesses desired bit value from wire represented in C code variable used for hierarchical generation.
Returns:
str: C code for assignment of one wire to another.
str: C code bitwise shift to get desired bit value position from this wire or wire variable's constant bit value 0/1.
"""
return f" {self.name} = {name};\n"
if self.is_const():
return f"({self.c_const})"
else:
return f"(({self.prefix} >> {self.index}) & 0x01)"
def return_wire_value_c(self, offset: int = 0):
"""Retrieve bit value from wire and shift it to desired position for storing it within a bus.
def return_wire_value_c_flat(self, offset: int = 0):
"""Retrieves desired bit value from wire represented in C code variable and bitwise shifts it to desired position for storing it within a bus for flat generation.
Args:
offset (int, optional): Used to shift wire value in order to be stored in proper location in bus. Defaults to 0.
offset (int, optional): Used to shift wire value in order to be stored in proper location inside a bus. Defaults to 0.
Returns:
str: C code bitwise shift of retrieved wire value.
str: C code bitwise shift for storing (constant/variable) wire value at desired offset position.
"""
return f"({self.name} & 0x01) << {offset}"
if self.is_const():
return f"({self.c_const}) << {offset};\n"
else:
return f"(({self.name} >> 0) & 0x01) << {offset};\n"
def return_wire_value_c_hier(self, offset: int = 0):
"""Retrieves desired bit value from wire represented in C code variable and bitwise shifts it to desired position for storing it within a bus for hierarchical generation.
Args:
offset (int, optional): Used to shift wire value in order to be stored in proper location inside a bus. Defaults to 0.
Returns:
str: C code bitwise shift for storing (constant/variable) wire value at desired offset position.
"""
if self.is_const():
return f"({self.c_const}) << {offset};\n"
else:
return f"(({self.prefix} >> {self.index}) & 0x01) << {offset};\n"
""" VERILOG CODE GENERATION """
def get_declaration_v(self):
"""Wire declaration in Verilog code.
def get_declaration_v_flat(self):
"""Wire declaration for flat Verilog code.
Returns:
str: Verilog code for declaration of wire's name.
str: Empty string if Verilog code wire is carrying constant value (constant value is used in situ) or returns Verilog code for declaration and initialization of wire's name.
"""
return f" wire {self.name};\n"
def get_assign_v(self, name: str, offset: int = 0, array: bool = False):
"""Assignment of wire value to another desired wire in Verilog.
Args:
name (str): Name of wire/bus to assign value from.
offset (int, optional): Used to retrieve desired wire from a bus. Defaults to 0.
array (bool, optional): Tells whether wire value is assigned from within a bus or from basic wire. Defaults to False.
Returns:
str: Verilog code for wire assignment.
"""
if array is True:
return f" assign {self.name} = {name}[{offset}];\n"
if self.is_const():
return ""
else:
return f" assign {self.name} = {name};\n"
return f" wire {self.name};\n"
def get_declaration_v_hier(self):
"""Wire declaration for hierarchical Verilog code.
Returns:
str: Empty string if Verilog code wire is carrying constant value (constant value is used in situ) or returns Verilog code for declaration and initialization of wire's name.
"""
if self.is_const():
return ""
else:
return f" wire [0:0] {self.name};\n"
def get_wire_value_v_flat(self):
"""Accesses bit value from wire represented in Verilog code variable used for flat generation.
Returns:
str: Verilog code to get bit value from this wire or to get constant wire's bit value 0/1.
"""
if self.is_const():
return self.v_const
else:
return self.name
def get_wire_value_v_hier(self):
"""Accesses bit value from wire represented in Verilog code variable used for hierarchical generation.
Returns:
str: Verilog code to get bit value from this wire or to get constant wire's bit value 0/1.
"""
if self.is_const():
return self.v_const
else:
return f"{self.prefix}[{self.index}]"
def return_wire_value_v_flat(self):
"""Retrieves bit value from wire represented in Verilog code variable for storing it within a bus for flat generation.
Returns:
str: Verilog code for retrieving (constant/variable) wire value (and assign it at desired bus offset position).
"""
if self.is_const():
return f"{self.v_const};\n"
else:
return f"{self.name};\n"
def return_wire_value_v_hier(self):
"""Retrieves bit value from bus's wires and stores them in bus's corresponding Verilog variable at proper offset bit position in the bus for hierarchical generation.
Returns:
str: Verilog code for retrieving (constant/variable) wire value used for assigning it into bus represented in Verilog code variable.
"""
if self.is_const():
return f"{self.v_const};\n"
else:
return f"{self.prefix}[{self.index}];\n"
""" BLIF CODE GENERATION """
def get_declaration_blif(self, name: str = "", offset: int = 0, array: bool = False):
def get_declaration_blif(self, prefix: str = "", offset: int = 0, array: bool = False):
"""Wire declaration in Blif code.
Declares basic wire name if wire is not a part of a bus,
or declares wire by an offset of its position within the input bus.
Declares basic wire name if wire is not part of a bus
or declares wire by an offset of its position within the bus.
Args:
name (str, optional): Name of a bus to be declared (if array is True). Defaults to "".
prefix (str, optional): Bus prefix of which this wire is a part off. Defaults to "".
offset (int, optional): Offset wire location within a bus. Defaults to 0.
array (bool, optional): Tells whether a basic wire or a wire from within a bus is to be declared. Defaults to False.
@ -103,23 +176,116 @@ class Wire():
str: Blif code for declaration of a wire.
"""
if array is True:
return f" {name}[{offset}]"
return f"{prefix}[{offset}]"
else:
return f" {self.name}"
return f"{self.name}"
def get_assign_blif(self, name: str, output: bool = False):
"""Assignment of wire value to another desired wire in Blif.
def get_assign_blif(self, prefix: str, output: bool = False):
"""Assignment of wire value to another desired wire in Blif code.
This wire's value is either assigned to desired output bus wire (represented by `prefix` name) when `output`=True.
Otherwise the wire value at desired bus position (represented by `prefix` name) is assigned to this wire when `output`=False.
Args:
name (str): Name of the source/destination wire to be assigned to.
output (bool, optional): Whether 'name' represents the destination or the source wire in the assignment. Defaults to False.
prefix (str): Name of the source/destination bus wire to be assigned from/to.
output (bool, optional): Whether `prefix` represents the destination or the source wire in the assignment. Defaultly it symbolizes the source. Defaults to False.
Returns:
str: Blif code for assignment of one wire to another.
"""
if output is True:
return f".names {self.name} {name}\n" + \
f"1 1\n"
if self.is_const():
return f".names {self.blif_const} {prefix}\n" + \
f"1 1\n"
else:
return f".names {self.name} {prefix}\n" + \
f"1 1\n"
else:
return f".names {name} {self.name}\n" + \
f"1 1\n"
if self.is_const():
return "\n"
else:
return f".names {prefix} {self.name}\n" + \
f"1 1\n"
def get_wire_value_blif(self):
"""Accesses bit value from wire represented in Blif code.
Used for assignment of specific one bit circuit/gate values to their respective parameters
in hierarchical Blif subcomponents generation.
Returns:
str: Blif code to get bit value from this wire or to get constant wire's bit value 0/1.
"""
if self.is_const():
return self.blif_const
elif self.parent_bus is not None and self.parent_bus.N > 1:
return self.name
else:
return self.prefix
# Wires with constant values #
class ConstantWireValue0(Wire):
"""Class representing wire carrying constant value 0 used to interconnect components.
Description of the __init__ method.
Method fills in desired constant wire's attributes regarding its values for individual representations.
Args:
name (str, optional): Custom constant wire name and prefix (used for generation of circuits that use constants as inputs). Defaults to "".
"""
def __init__(self, name: str = ""):
self.name = "constant_value_0" if name == "" else name
self.prefix = "constant_value_0" if name == "" else name
self.index = 0
self.value = 0
self.parent_bus = None
self.c_const = "0x00"
self.v_const = "1'b0"
self.blif_const = "gnd"
# Constant wire id for CGP generation
self.cgp_const = 0
@staticmethod
def is_const():
"""Information whether wire carries constant value.
Returns:
bool: True, because constant wire carries a constant value 0.
"""
return True
class ConstantWireValue1(Wire):
"""Class representing wire carrying constant value 1 used to interconnect components.
Description of the __init__ method.
Method fills in desired constant wire's attributes regarding its values for individual representations.
Args:
name (str, optional): Custom constant wire name and prefix (used for generation of circuits that use constants as inputs). Defaults to "".
"""
def __init__(self, name: str = ""):
self.name = "constant_value_1" if name == "" else name
self.prefix = "constant_value_1" if name == "" else name
self.index = 0
self.value = 1
self.parent_bus = None
self.c_const = "0x01"
self.v_const = "1'b1"
self.blif_const = "vdd"
# Constant wire id for CGP generation
self.cgp_const = 1
@staticmethod
def is_const():
"""Information whether wire carries constant value.
Returns:
bool: True, because constant wire carries a constant value 1.
"""
return True

139
yosys_equiv_check.sh Normal file
View File

@ -0,0 +1,139 @@
#!/bin/bash
# Script enables formal verification and equivalence of Verilog/BLIF circuit designs
# using Yosys Open SYnthesis Suite by Clifford Wolf.
# For more information, please visit: http://www.clifford.at/yosys/documentation.html
# Echo script help
help () {
echo ""
echo " HELP "
echo ""
echo " SCRIPT FOR CHECKING FORMAL EQUIVALENCE BETWEEN VERILOG AND BLIF DESIGNS USING YOSYS "
echo
echo "Input files should have the same name used for corresponding file types filenames as well"
echo "as for their inner design's top module names."
echo "Formal verification and equivalence of Verilog/BLIF circuit designs achieved using Yosys"
echo "Open SYnthesis Suite."
echo ""
echo "Input parameters:"
echo "-h, --help"
echo " help information"
echo "-v 'verilog_file', --verilog_file 'verilog_file'"
echo " specifies input Verilog design file"
echo "-b 'blif_file', --blif_file 'blif_file'"
echo " specifies input BLIF design file"
echo "-H, --hier"
echo " specifies whether designs are in hierarchical representation (default is flat)"
}
# No input parameters present, echo help and exit.
if [[ "$#" -eq 0 ]]; then
help
exit 0
fi
# Defaultly flat designs are considered
HIERARCHICAL=false
# Parsing and extraction of input parameters and their arguments into variables.
while [[ "$#" -gt 0 ]] ; do
case "$1" in
"-v" | "--verilog_file")
if [[ -f "$2" && "$2" == *.v ]]; then
VERILOG_FILE=$2
else
echo "$2 is not a Verilog file!";
exit 1
fi
shift 2;;
"-b" | "--blif_file")
if [[ -f "$2" && "$2" == *.blif ]]; then
BLIF_FILE="$2"
else
echo "$2 is not a BLIF file!";
exit 1
fi
shift 2;;
"-H" | "--hier")
HIERARCHICAL=true
shift 1;;
"-h" | "--help")
help
exit 0;;
*)
echo "Unknown input parameter $1!"
echo
echo "Type -h | --help for more information."
exit 1;;
esac
done
# Check if both files compulsory parameters are set and if they have the same name.
# To proper equiv check both designs top modules must be of same names (assumption that filename == top module name)
if [[ -z "$VERILOG_FILE" || -z "$BLIF_FILE" ]]; then
[ -z "$VERILOG_FILE" ] && echo "Missing compulsory Verilog file for comparison!"
[ -z "$BLIF_FILE" ] && echo "Missing compulsory BLIF file for comparison!"
echo
echo "Type -h | --help for more information."
exit 1
else
TOP_MODULE=$(echo $VERILOG_FILE | cut -c1-"$((${#VERILOG_FILE}-2))")
if [[ "$TOP_MODULE" != $(echo $BLIF_FILE | cut -c1-"$((${#BLIF_FILE}-5))") ]]; then
echo "Input files have different names! Do they describe the same circuit design?"
echo "For proper equivalence check, both designs should have the same name used for their filenames and also for their top module designs."
echo
echo "Type -h | --help for more information."
exit 2
fi
fi
# Formal verification with equiv_* for flat designs
if [ "$HIERARCHICAL" = false ]; then
yosys -p "
# Gold design
read_verilog $VERILOG_FILE
prep -top $TOP_MODULE
splitnets -ports;;
design -stash gold
# Gate design
read_blif $BLIF_FILE
prep -top $TOP_MODULE
design -stash gate
# Prove equivalence
design -copy-from gold -as gold $TOP_MODULE
design -copy-from gate -as gate $TOP_MODULE
equiv_make gold gate equiv
hierarchy -top equiv
equiv_simple
equiv_status -assert
"
# Formal verification with equiv_* for hierarchical designs
else
yosys -p "
# Gold design
read_verilog $VERILOG_FILE
prep -top $TOP_MODULE
flatten
splitnets -ports;;
design -stash gold
# Gate design
read_blif $BLIF_FILE
prep -top $TOP_MODULE
flatten
splitnets -ports;;
design -stash gate
# Prove equivalence
design -copy-from gold -as gold $TOP_MODULE
design -copy-from gate -as gate $TOP_MODULE
equiv_make gold gate equiv
hierarchy -top equiv
equiv_simple
equiv_status -assert
"
fi