mirror of
https://github.com/ehw-fit/ariths-gen.git
synced 2025-04-08 08:12:11 +01:00
Python eval (#4)
* #3 basic clean up arithmetic circuit and general circuit * #3 implementation of python generator * #3 pytest in actions * #3 pytest in actions fix
This commit is contained in:
parent
995107eecc
commit
152a6b1583
20
.github/workflows/generate.yml
vendored
20
.github/workflows/generate.yml
vendored
@ -70,6 +70,26 @@ jobs:
|
||||
bash test_mac.sh
|
||||
cd ..
|
||||
|
||||
|
||||
test_python:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [ '3.6.x', '3.7.x', '3.8.x', '3.9.x', '3.10.0-rc.2' ]
|
||||
name: Python ${{ matrix.python-version }} test
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: x64
|
||||
- run: python -m pip install numpy pytest
|
||||
- name: Run pytest
|
||||
run: |
|
||||
pytest
|
||||
|
||||
# Only on main thread
|
||||
documentation:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -7,8 +7,10 @@ from ariths_gen.wire_components import (
|
||||
Bus
|
||||
)
|
||||
|
||||
from ariths_gen.core.arithmetic_circuits.general_circuit import GeneralCircuit
|
||||
|
||||
class ArithmeticCircuit():
|
||||
|
||||
class ArithmeticCircuit(GeneralCircuit):
|
||||
"""Class represents a general arithmetic circuit and ensures their generation to various representations.
|
||||
|
||||
The __init__ method fills some mandatory attributes concerning arithmetic circuit
|
||||
@ -16,6 +18,8 @@ class ArithmeticCircuit():
|
||||
"""
|
||||
|
||||
def __init__(self, a, b, prefix: str, name: str, out_N: int, inner_component: bool = False, one_bit_circuit: bool = False):
|
||||
|
||||
super().__init__(prefix, name, out_N, inner_component, inputs=[a, b])
|
||||
if one_bit_circuit is False:
|
||||
if prefix == "":
|
||||
self.prefix = name
|
||||
@ -38,217 +42,7 @@ class ArithmeticCircuit():
|
||||
# N output wires for given circuit
|
||||
self.out = Bus(self.prefix+"_out", out_N, out_bus=True)
|
||||
|
||||
self.components = []
|
||||
self.circuit_wires = []
|
||||
self.circuit_gates = []
|
||||
self.c_data_type = "uint64_t"
|
||||
|
||||
def add_component(self, component):
|
||||
"""Adds a component into list of circuit's inner subcomponents.
|
||||
|
||||
Args:
|
||||
component: Subcomponent to be added into list of components composing described circuit.
|
||||
"""
|
||||
self.components.append(component)
|
||||
|
||||
def get_previous_component(self, number: int = 1):
|
||||
"""Retrieves previously added composite subcomponent from circuit's list of components.
|
||||
|
||||
Args:
|
||||
number (int, optional): Offset indicating which lastly added component will be retrieved. Defaults to 1.
|
||||
|
||||
Returns:
|
||||
component: Desired previously added composite component.
|
||||
"""
|
||||
return self.components[-number]
|
||||
|
||||
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 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.
|
||||
"""
|
||||
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 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, TwoInputLogicGate):
|
||||
continue
|
||||
elif isinstance(getattr(c, 'a'), Wire):
|
||||
one_bit_comps.append(c)
|
||||
else:
|
||||
one_bit_comps.extend(c.get_one_bit_components())
|
||||
|
||||
return one_bit_comps
|
||||
|
||||
def get_multi_bit_components(self):
|
||||
"""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, TwoInputLogicGate):
|
||||
continue
|
||||
elif isinstance(getattr(c, 'a'), Wire):
|
||||
continue
|
||||
else:
|
||||
multi_bit_comps.append(c)
|
||||
return multi_bit_comps
|
||||
|
||||
@staticmethod
|
||||
def get_unique_types(components: list):
|
||||
"""Retrieves just the unique representatives of class types present inside the provided components list.
|
||||
|
||||
Args:
|
||||
components (list): List of components to be filtered.
|
||||
|
||||
Returns:
|
||||
list: List of unique composite class types.
|
||||
"""
|
||||
return list({type(c): c for c in components}.values())
|
||||
|
||||
def get_component_types(self):
|
||||
"""Retrieves a list of all the unique types of subcomponents composing the circuit.
|
||||
|
||||
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.
|
||||
"""
|
||||
gate_comps = self.get_unique_types(components=self.get_circuit_gates())
|
||||
one_bit_comps = self.get_unique_types(
|
||||
components=self.get_one_bit_components())
|
||||
multi_bit_comps = self.get_unique_types(
|
||||
components=self.get_multi_bit_components())
|
||||
|
||||
all_components = gate_comps + one_bit_comps + multi_bit_comps
|
||||
return all_components
|
||||
|
||||
def get_sum_wire(self):
|
||||
"""Get output wire carrying sum value.
|
||||
|
||||
Returns:
|
||||
Wire: Return sum wire.
|
||||
"""
|
||||
return self.out.get_wire(0)
|
||||
|
||||
def get_carry_wire(self):
|
||||
"""Get output wire carrying carry out value.
|
||||
|
||||
Returns:
|
||||
Wire: Return carry out wire.
|
||||
"""
|
||||
return self.out.get_wire(1)
|
||||
|
||||
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 = []
|
||||
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)))
|
||||
|
||||
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 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)))
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
wire (Wire): Wire to retrieve index position of.
|
||||
|
||||
Returns:
|
||||
int: Wire's index position number within the circuit.
|
||||
"""
|
||||
if wire.is_const():
|
||||
return wire.cgp_const
|
||||
else:
|
||||
for w in self.circuit_wires:
|
||||
if wire.name == w[1]:
|
||||
return w[2]
|
||||
|
||||
""" 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 arithmetic circuit's interface in C code.
|
||||
|
||||
@ -257,56 +51,6 @@ 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_declaration_c_flat(self):
|
||||
"""Generates flat C code declaration of input/output circuit wires.
|
||||
|
||||
Returns:
|
||||
str: Flat C code arithmetic circuit's wires declaration.
|
||||
"""
|
||||
return f"".join([c.get_declaration_c_flat() for c in self.components])
|
||||
|
||||
def get_init_c_flat(self):
|
||||
"""Generates flat C code initialization and assignment of corresponding arithmetic circuit's input/output wires.
|
||||
|
||||
Returns:
|
||||
str: Flat C code initialization of arithmetic circuit wires.
|
||||
"""
|
||||
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: Flat C code containing output bus wires assignment.
|
||||
"""
|
||||
return self.out.return_bus_wires_values_c_flat()
|
||||
|
||||
# Generating flat C code representation of circuit
|
||||
def get_c_code_flat(self, file_object):
|
||||
"""Generates flat C code representation of corresponding arithmetic 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}")
|
||||
|
||||
# HIERARCHICAL C #
|
||||
def get_function_blocks_c(self):
|
||||
"""Generates hierarchical C code representation of all subcomponents function blocks present in corresponding arithmetic circuit.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical C code of all subcomponents function blocks description.
|
||||
"""
|
||||
# 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])
|
||||
|
||||
def get_function_block_c(self):
|
||||
"""Generates hierarchical C code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
|
||||
|
||||
@ -320,85 +64,6 @@ class ArithmeticCircuit():
|
||||
N=self.N, prefix="b"), name=circuit_prefix)
|
||||
return f"{circuit_block.get_circuit_c()}\n\n"
|
||||
|
||||
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([c.get_declaration_c_hier() for c in self.components])
|
||||
|
||||
def get_declaration_c_hier(self):
|
||||
"""Generates hierarchical C code declaration of corresponding subcomponent input/output wires inside the upper component.
|
||||
|
||||
Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present
|
||||
inside the upper component from which function block has been invoked.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical C code of subcomponent arithmetic circuit's wires declaration.
|
||||
"""
|
||||
return f" {self.c_data_type} {self.a.prefix} = 0;\n" + \
|
||||
f" {self.c_data_type} {self.b.prefix} = 0;\n" + \
|
||||
f" {self.c_data_type} {self.out.prefix} = 0;\n"
|
||||
|
||||
def get_init_c_hier(self):
|
||||
"""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 "".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 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.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical C code of subcomponent's C function invocation and output assignment.
|
||||
"""
|
||||
# Getting name of circuit type for proper C code generation without affecting actual generated composition
|
||||
circuit_type = self.prefix.replace(circuit_prefix+"_", "")
|
||||
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.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical C code containing output bus wires assignment.
|
||||
"""
|
||||
return self.out.return_bus_wires_values_c_hier()
|
||||
|
||||
def get_circuit_c(self):
|
||||
"""Generates hierarchical C code subcomponent's function block.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical C code of subcomponent's function block.
|
||||
"""
|
||||
return f"{self.get_prototype_c()}" + \
|
||||
f"{self.out.get_declaration_c()}" + \
|
||||
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}"
|
||||
|
||||
# Generating hierarchical C code representation of circuit
|
||||
def get_c_code_hier(self, file_object):
|
||||
"""Generates hierarchical C code representation of corresponding arithmetic 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_function_blocks_c())
|
||||
file_object.write(self.get_circuit_c())
|
||||
|
||||
""" VERILOG CODE GENERATION """
|
||||
# FLAT VERILOG #
|
||||
|
||||
@ -409,55 +74,7 @@ class ArithmeticCircuit():
|
||||
str: Module's name and parameters in Verilog code.
|
||||
"""
|
||||
return f"module {self.prefix}(input [{self.N-1}:0] {self.a.prefix}, input [{self.N-1}:0] {self.b.prefix}, output [{self.out.N-1}:0] {self.out.prefix});\n"
|
||||
|
||||
def get_declaration_v_flat(self):
|
||||
"""Generates flat Verilog code declaration of input/output circuit wires.
|
||||
|
||||
Returns:
|
||||
str: Flat Verilog code arithmetic circuit's wires declaration.
|
||||
"""
|
||||
return f"".join([c.get_declaration_v_flat() for c in self.components])
|
||||
|
||||
def get_init_v_flat(self):
|
||||
"""Generates flat Verilog code initialization and assignment of corresponding arithmetic circuit's input/output buses wires.
|
||||
|
||||
Returns:
|
||||
str: Flat Verilog code initialization of arithmetic circuit wires.
|
||||
"""
|
||||
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: Flat Verilog code containing output bus wires assignment.
|
||||
"""
|
||||
return self.out.return_bus_wires_values_v_flat()
|
||||
|
||||
# Generating flat Verilog code representation of circuit
|
||||
def get_v_code_flat(self, file_object):
|
||||
"""Generates flat Verilog code representation of corresponding arithmetic 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() + "\n")
|
||||
file_object.write(self.get_function_out_v_flat())
|
||||
file_object.write(f"endmodule")
|
||||
|
||||
# HIERARCHICAL VERILOG #
|
||||
def get_function_blocks_v(self):
|
||||
"""Generates hierarchical Verilog code representation of all subcomponents function blocks present in corresponding arithmetic circuit.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Verilog code of all subcomponents function blocks description.
|
||||
"""
|
||||
# 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])
|
||||
|
||||
def get_function_block_v(self):
|
||||
"""Generates hierarchical Verilog code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
|
||||
|
||||
@ -470,36 +87,6 @@ class ArithmeticCircuit():
|
||||
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||
N=self.N, prefix="b"), name=circuit_prefix)
|
||||
return f"{circuit_block.get_circuit_v()}\n\n"
|
||||
|
||||
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([c.get_declaration_v_hier() for c in self.components])
|
||||
|
||||
def get_declaration_v_hier(self):
|
||||
"""Generates hierarchical Verilog code declaration of corresponding subcomponent input/output wires inside the upper component.
|
||||
|
||||
Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present
|
||||
inside the upper component from which function block has been invoked.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Verilog code of subcomponent arithmetic circuit's wires declaration.
|
||||
"""
|
||||
return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \
|
||||
f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \
|
||||
f" wire [{self.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 wires.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Verilog code initialization of arithmetic circuit wires.
|
||||
"""
|
||||
return "".join([c.get_gate_invocation_v() if isinstance(c, TwoInputLogicGate) else c.get_out_invocation_v(circuit_prefix=self.prefix) for c in self.components])
|
||||
|
||||
def get_out_invocation_v(self, circuit_prefix: str):
|
||||
"""Generates hierarchical Verilog code invocation of corresponding arithmetic circuit's generated function block.
|
||||
|
||||
@ -524,146 +111,6 @@ class ArithmeticCircuit():
|
||||
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}(.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.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.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Verilog code containing output bus wires assignment.
|
||||
"""
|
||||
return self.out.return_bus_wires_values_v_hier()
|
||||
|
||||
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()}" + \
|
||||
f"{self.get_declarations_v_hier()}\n" + \
|
||||
f"{self.get_init_v_hier()}\n" + \
|
||||
f"{self.get_function_out_v_hier()}" + \
|
||||
f"endmodule"
|
||||
|
||||
# Generating hierarchical Verilog code representation of circuit
|
||||
def get_v_code_hier(self, file_object):
|
||||
"""Generates hierarchical Verilog code representation of corresponding arithmetic circuit.
|
||||
|
||||
Args:
|
||||
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
|
||||
"""
|
||||
file_object.write(self.get_function_blocks_v())
|
||||
file_object.write(self.get_circuit_v())
|
||||
|
||||
""" BLIF CODE GENERATION """
|
||||
# FLAT BLIF #
|
||||
|
||||
def get_prototype_blif(self):
|
||||
"""Generates Blif code model name of described arithmetic circuit.
|
||||
|
||||
Returns:
|
||||
str: Model's name in Blif code.
|
||||
"""
|
||||
return f".model {self.prefix}\n"
|
||||
|
||||
def get_declaration_blif(self):
|
||||
"""Generates flat Blif code declaration of input/output circuit wires.
|
||||
|
||||
Returns:
|
||||
str: Flat Blif code containing declaration of circuit's wires.
|
||||
"""
|
||||
if self.N == 1:
|
||||
return f".inputs {self.a.prefix} {self.b.prefix}\n" + \
|
||||
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
f".names gnd\n0\n"
|
||||
else:
|
||||
return f".inputs{self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}\n" + \
|
||||
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
f".names gnd\n0\n"
|
||||
|
||||
def get_function_blif_flat(self):
|
||||
"""Generates flat Blif code with invocation of subcomponents logic gates functions via their corresponding truth tables.
|
||||
|
||||
Returns:
|
||||
str: Flat Blif code containing invocation of inner subcomponents logic gates Boolean functions.
|
||||
"""
|
||||
return "".join(c.get_function_blif_flat() for c in self.components)
|
||||
|
||||
def get_function_out_blif(self):
|
||||
"""Generates flat Blif code assignment of corresponding arithmetic circuit's output bus wires.
|
||||
|
||||
Returns:
|
||||
str: Flat Blif code containing output bus wires assignment.
|
||||
"""
|
||||
return f"{self.out.get_wire_assign_blif(output=True)}"
|
||||
|
||||
# Generating flat BLIF code representation of circuit
|
||||
def get_blif_code_flat(self, file_object):
|
||||
"""Generates flat Blif code representation of corresponding arithmetic 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(self.get_function_out_blif())
|
||||
file_object.write(f".end\n")
|
||||
|
||||
# HIERARCHICAL BLIF #
|
||||
def get_invocations_blif_hier(self):
|
||||
"""Generates hierarchical Blif code with invocations of subcomponents function blocks.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Blif code containing invocations of inner subcomponents function blocks.
|
||||
"""
|
||||
return "".join(c.get_invocation_blif_hier(circuit_prefix=self.prefix) for c in self.components)
|
||||
|
||||
def get_invocation_blif_hier(self, circuit_prefix: str):
|
||||
"""Generates hierarchical Blif code invocation of corresponding arithmetic circuit's generated function block.
|
||||
|
||||
Used for multi-bit subcomponent's modul invocation.
|
||||
|
||||
Args:
|
||||
circuit_prefix (str): Prefix name of the upper component from which function block is being invoked.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Blif code of subcomponent's model invocation and output assignment.
|
||||
"""
|
||||
# Getting name of circuit type for proper Blif code generation without affecting actual generated composition
|
||||
circuit_type = self.prefix.replace(circuit_prefix+"_", "")
|
||||
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.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.
|
||||
|
||||
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"{self.get_function_out_blif()}" + \
|
||||
f".end\n"
|
||||
|
||||
def get_function_blocks_blif(self):
|
||||
"""Generates hierarchical Blif code representation of all subcomponents function blocks present in corresponding arithmetic circuit.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Blif code of all subcomponents function blocks description.
|
||||
"""
|
||||
# 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]])
|
||||
|
||||
def get_function_block_blif(self):
|
||||
"""Generates hierarchical Blif code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
|
||||
|
||||
@ -674,64 +121,3 @@ class ArithmeticCircuit():
|
||||
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||
N=self.N, prefix="b"))
|
||||
return f"{circuit_block.get_circuit_blif()}"
|
||||
|
||||
# Generating hierarchical BLIF code representation of circuit
|
||||
def get_blif_code_hier(self, file_object):
|
||||
"""Generates hierarchical Blif code representation of corresponding arithmetic circuit.
|
||||
|
||||
Args:
|
||||
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
|
||||
"""
|
||||
file_object.write(self.get_circuit_blif()+"\n")
|
||||
file_object.write(self.get_function_blocks_blif())
|
||||
|
||||
""" CGP CODE GENERATION """
|
||||
# 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.
|
||||
|
||||
Returns:
|
||||
str: CGP chromosome parameters of described arithmetic circuit.
|
||||
"""
|
||||
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_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_id=self.get_circuit_wire_index(g.a), out_id=self.get_circuit_wire_index(g.out)) 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), out_id=self.get_circuit_wire_index(g.out)) for g in self.circuit_gates])
|
||||
|
||||
def get_outputs_cgp(self):
|
||||
"""Generates list of output wires indexes of described arithmetic circuit from MSB to LSB.
|
||||
|
||||
Returns:
|
||||
str: List of arithmetic circuit's output wire indexes.
|
||||
"""
|
||||
return "(" + ",".join([str(self.get_circuit_wire_index(o)) for o in self.out.bus]) + ")"
|
||||
|
||||
# Generating flat CGP chromosome representation of circuit
|
||||
def get_cgp_code_flat(self, file_object):
|
||||
"""Generates flat CGP chromosome representation of corresponding arithmetic circuit.
|
||||
|
||||
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_triplets_cgp())
|
||||
file_object.write(self.get_outputs_cgp())
|
||||
|
@ -7,6 +7,7 @@ from ariths_gen.wire_components import (
|
||||
Bus
|
||||
)
|
||||
|
||||
from io import StringIO
|
||||
|
||||
class GeneralCircuit():
|
||||
"""Class represents a general arithmetic circuit and ensures their generation to various representations.
|
||||
@ -28,6 +29,19 @@ class GeneralCircuit():
|
||||
self.circuit_wires = []
|
||||
self.circuit_gates = []
|
||||
self.c_data_type = "uint64_t"
|
||||
self.pyc = None # Python compiled function
|
||||
|
||||
def __call__(self, *args):
|
||||
if not self.pyc:
|
||||
buf = StringIO()
|
||||
self.get_python_code_flat(buf)
|
||||
|
||||
globs, locs = {}, {}
|
||||
exec(buf.getvalue(), globs, locs)
|
||||
#print(locs)
|
||||
self.pyc = locs[self.prefix]
|
||||
|
||||
return self.pyc(*args)
|
||||
|
||||
def add_component(self, component):
|
||||
"""Adds a component into list of circuit's inner subcomponents.
|
||||
@ -225,6 +239,46 @@ class GeneralCircuit():
|
||||
if wire.name == w[1]:
|
||||
return w[2]
|
||||
|
||||
""" PYTHON CODE GENERATION """
|
||||
# FLAT PYTHON #
|
||||
def get_prototype_python(self):
|
||||
"""Generates python code function header to describe corresponding arithmetic circuit's interface in python code.
|
||||
|
||||
Returns:
|
||||
str: Function's name and parameters in C code.
|
||||
"""
|
||||
return f"def {self.prefix}(" + ", ".join([f"{x.prefix}" for x in self.inputs]) + ")" + ":" + "\n"
|
||||
|
||||
def get_init_python_flat(self):
|
||||
"""Generates flat C code initialization and assignment of corresponding arithmetic circuit's input/output wires.
|
||||
|
||||
Returns:
|
||||
str: Flat C code initialization of arithmetic circuit wires.
|
||||
"""
|
||||
return "".join([c.get_assign_python_flat() if isinstance(c, TwoInputLogicGate) else c.get_init_python_flat() for c in self.components])
|
||||
|
||||
def get_function_out_python_flat(self):
|
||||
"""Generates flat C code assignment of corresponding arithmetic circuit's output bus wires.
|
||||
|
||||
Returns:
|
||||
str: Flat C code containing output bus wires assignment.
|
||||
"""
|
||||
return self.out.return_bus_wires_values_python_flat()
|
||||
|
||||
# Generating flat C code representation of circuit
|
||||
def get_python_code_flat(self, file_object):
|
||||
"""Generates flat Python code representation of corresponding arithmetic circuit.
|
||||
|
||||
Args:
|
||||
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
|
||||
"""
|
||||
file_object.write(self.get_prototype_python())
|
||||
#file_object.write(self.out.get_declaration_python())
|
||||
#file_object.write(self.get_declaration_python_flat()+"\n")
|
||||
file_object.write(self.get_init_python_flat()+"\n")
|
||||
file_object.write(self.get_function_out_python_flat())
|
||||
file_object.write(f" return {self.out.prefix}"+"\n")
|
||||
|
||||
""" C CODE GENERATION """
|
||||
# FLAT C #
|
||||
@staticmethod
|
||||
|
@ -195,6 +195,19 @@ class TwoInputLogicGate():
|
||||
else:
|
||||
return f" {self.out.name} = {self.gate_type}({self.a.get_wire_value_c_hier()}, {self.b.get_wire_value_c_hier()});\n"
|
||||
|
||||
def get_assign_python_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"
|
||||
|
||||
""" VERILOG CODE GENERATION """
|
||||
# FLAT VERILOG #
|
||||
def get_prototype_v_flat(self):
|
||||
|
@ -87,6 +87,14 @@ class Bus():
|
||||
# Nakonec je potřeba napojit výstup adderu na výstup mac
|
||||
[self.connect(o, connecting_bus.get_wire(o), inserted_wire_desired_index=o) for o in range(start_connection_pos, end_connection_pos)]
|
||||
|
||||
def return_bus_wires_values_python_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 assigning wire values into bus represented in C code variable.
|
||||
"""
|
||||
return "".join([f" {self.prefix} = 0\n"] + [f" {self.prefix} |= {w.return_wire_value_python_flat(offset=self.bus.index(w))}" for w in self.bus])
|
||||
|
||||
""" C CODE GENERATION """
|
||||
def get_declaration_c(self):
|
||||
"""Bus declaration in C code.
|
||||
|
@ -26,6 +26,21 @@ class Wire():
|
||||
"""
|
||||
return False
|
||||
|
||||
""" Python CODE GENERATION """
|
||||
def return_wire_value_python_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 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.name} >> 0) & 0x01) << {offset}\n"
|
||||
|
||||
""" C CODE GENERATION """
|
||||
def get_declaration_c(self):
|
||||
"""Wire declaration in C code.
|
||||
|
121
tests/test_all.py
Normal file
121
tests/test_all.py
Normal file
@ -0,0 +1,121 @@
|
||||
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
|
||||
from ariths_gen.core.arithmetic_circuits import GeneralCircuit
|
||||
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder,
|
||||
UnsignedPGRippleCarryAdder,
|
||||
UnsignedRippleCarryAdder,
|
||||
SignedCarryLookaheadAdder,
|
||||
SignedPGRippleCarryAdder,
|
||||
SignedRippleCarryAdder,
|
||||
UnsignedCarrySkipAdder,
|
||||
SignedCarrySkipAdder,
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.multipliers import (
|
||||
UnsignedDaddaMultiplier,
|
||||
UnsignedArrayMultiplier,
|
||||
UnsignedWallaceMultiplier,
|
||||
SignedArrayMultiplier,
|
||||
SignedDaddaMultiplier,
|
||||
SignedWallaceMultiplier,
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
|
||||
def test_unsigned_mul():
|
||||
""" Test unsigned multipliers """
|
||||
N = 7
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
av = np.arange(2**N)
|
||||
bv = av.reshape(-1, 1)
|
||||
expected = av * bv
|
||||
|
||||
for c in [ UnsignedDaddaMultiplier, UnsignedArrayMultiplier, UnsignedWallaceMultiplier]:
|
||||
mul = c(a, b)
|
||||
r = mul(av, bv)
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
|
||||
def test_signed_mul():
|
||||
""" Test signed multipliers """
|
||||
N = 7
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
av = np.arange(-(2**(N-1)), 2**(N-1))
|
||||
bv = av.reshape(-1, 1)
|
||||
expected = av * bv
|
||||
|
||||
for c in [ SignedDaddaMultiplier, SignedArrayMultiplier, SignedWallaceMultiplier]:
|
||||
mul = c(a, b)
|
||||
r = mul(av, bv)
|
||||
|
||||
r[r >= 2**(2*N-1)] -= 2**(2*N) # hack!!! two's complement not implemented yet
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
|
||||
def test_unsigned_add():
|
||||
""" Test unsigned adders """
|
||||
N = 7
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
av = np.arange(2**N)
|
||||
bv = av.reshape(-1, 1)
|
||||
expected = av + bv
|
||||
|
||||
for c in [UnsignedCarryLookaheadAdder, UnsignedPGRippleCarryAdder, UnsignedRippleCarryAdder, UnsignedCarrySkipAdder]:
|
||||
mul = c(a, b)
|
||||
r = mul(av, bv)
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
|
||||
def test_signed_add():
|
||||
""" Test signed adders """
|
||||
N = 7
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
av = np.arange(-(2**(N-1)), 2**(N-1))
|
||||
bv = av.reshape(-1, 1)
|
||||
expected = av + bv
|
||||
|
||||
for c in [SignedCarryLookaheadAdder, SignedPGRippleCarryAdder, SignedRippleCarryAdder, SignedCarrySkipAdder]:
|
||||
mul = c(a, b)
|
||||
r = mul(av, bv)
|
||||
r[r >= 2**(N)] -= 2**(N+1) # hack!!! two's complement not implemented yet
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
def test_mac():
|
||||
class MAC(GeneralCircuit):
|
||||
def __init__(self, a: Bus, b: Bus, r: Bus, prefix: str = "", name: str = "mac", **kwargs):
|
||||
super().__init__(prefix=prefix, name=name, out_N=2*a.N+1, inputs=[a, b, r], **kwargs)
|
||||
assert a.N == b.N
|
||||
assert r.N == 2 * a.N
|
||||
|
||||
self.mul = self.add_component(UnsignedArrayMultiplier(a=a, b=b, prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True))
|
||||
self.add = self.add_component(UnsignedRippleCarryAdder(a=r, b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True))
|
||||
self.out.connect_bus(connecting_bus=self.add.out)
|
||||
|
||||
# usage
|
||||
mymac = MAC(Bus("a", 4), Bus("b", 4), Bus("acc", 8))
|
||||
|
||||
av = np.arange(2**4)
|
||||
bv = np.arange(2**4).reshape(-1, 1)
|
||||
cv = np.arange(2**8).reshape(-1, 1, 1)
|
||||
|
||||
r = mymac(av, bv, cv)
|
||||
expected = (av * bv) + cv
|
||||
np.testing.assert_array_equal(r, expected)
|
Loading…
x
Reference in New Issue
Block a user