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:
parent
068def0226
commit
8e950fc51f
@ -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"))
|
||||
|
||||
|
@ -10,5 +10,6 @@ from ariths_gen.one_bit_circuits import(
|
||||
|
||||
from ariths_gen.multi_bit_circuits import(
|
||||
adders,
|
||||
multipliers
|
||||
multipliers,
|
||||
dividers
|
||||
)
|
@ -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
|
||||
)
|
7
ariths_gen/core/arithmetic_circuits/__init__.py
Normal file
7
ariths_gen/core/arithmetic_circuits/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from .arithmetic_circuit import (
|
||||
ArithmeticCircuit
|
||||
)
|
||||
|
||||
from .multiplier_circuit import (
|
||||
MultiplierCircuit
|
||||
)
|
@ -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()
|
@ -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.
|
6
ariths_gen/core/logic_gate_circuits/__init__.py
Normal file
6
ariths_gen/core/logic_gate_circuits/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
from .logic_gate_circuit import (
|
||||
MultipleInputLogicGate,
|
||||
TwoInputLogicGate,
|
||||
TwoInputInvertedLogicGate,
|
||||
OneInputLogicGate
|
||||
)
|
753
ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py
Normal file
753
ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py
Normal 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)
|
7
ariths_gen/core/one_bit_circuits/__init__.py
Normal file
7
ariths_gen/core/one_bit_circuits/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from .two_input_one_bit_circuit import (
|
||||
TwoInputOneBitCircuit
|
||||
)
|
||||
|
||||
from .three_input_one_bit_circuit import (
|
||||
ThreeInputOneBitCircuit
|
||||
)
|
213
ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py
Normal file
213
ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py
Normal 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}}"
|
355
ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py
Normal file
355
ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py
Normal 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}}"
|
@ -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}}"
|
@ -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}}"
|
@ -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
|
||||
)
|
@ -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)
|
||||
|
180
ariths_gen/multi_bit_circuits/adders/carry_skip_adder.py
Normal file
180
ariths_gen/multi_bit_circuits/adders/carry_skip_adder.py
Normal 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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
3
ariths_gen/multi_bit_circuits/dividers/__init__.py
Normal file
3
ariths_gen/multi_bit_circuits/dividers/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from ariths_gen.multi_bit_circuits.dividers.array_divider import(
|
||||
ArrayDivider
|
||||
)
|
122
ariths_gen/multi_bit_circuits/dividers/array_divider.py
Normal file
122
ariths_gen/multi_bit_circuits/dividers/array_divider.py
Normal 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:1MUX├─►│2:1MUX│
|
||||
│ └───┬──┘ └───┬──┘ A1 B0
|
||||
│ MX0│ B2 MX1│ B1 │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ ┌─▼─▼──┐ ┌─▼─▼──┐ ┌─▼──▼─┐
|
||||
│ │ │ │ │ │ │
|
||||
│ ┌─┤ FS │◄─┤ FS │◄─┤ FS │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ └──────┘ └───┬──┘ └───┬──┘
|
||||
│ │ │ │MX1 │ │A1
|
||||
│ │ ┌─▼───▼┐ ┌─▼───▼┐
|
||||
│ ├────────────►│2:1MUX├─►│2: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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -1,5 +1,4 @@
|
||||
from .logic_gates import (
|
||||
LogicGate,
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -1,2 +1,2 @@
|
||||
from .wires import Wire
|
||||
from .wires import Wire, ConstantWireValue0, ConstantWireValue1
|
||||
from .buses import Bus
|
@ -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])
|
||||
|
@ -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
139
yosys_equiv_check.sh
Normal 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
|
Loading…
x
Reference in New Issue
Block a user