Implemented CSA and Wallace tree multiplier composing of CSAs. Also did some code cleanup.

This commit is contained in:
Honza 2022-02-18 17:00:31 +01:00
parent 3c47407f80
commit 6f05db002e
12 changed files with 698 additions and 84 deletions

View File

@ -1,5 +1,6 @@
from .arithmetic_circuit import (
ArithmeticCircuit
ArithmeticCircuit,
ThreeInputArithmeticCircuit
)
from .general_circuit import (GeneralCircuit )

View File

@ -38,9 +38,6 @@ class ArithmeticCircuit(GeneralCircuit):
self.a = Bus(prefix=f"{a.prefix}", wires_list=a.bus)
self.b = Bus(prefix=f"{b.prefix}", wires_list=b.bus)
# N output wires for given circuit
self.out = Bus(self.prefix+"_out", out_N, out_bus=True, signed=self.signed)
""" C CODE GENERATION """
def get_prototype_c(self):
"""Generates C code function header to describe corresponding arithmetic circuit's interface in C code.
@ -76,3 +73,193 @@ class ArithmeticCircuit(GeneralCircuit):
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
f".names vdd\n1\n" + \
f".names gnd\n0\n"
class ThreeInputArithmeticCircuit(GeneralCircuit):
"""Class represents a general three input arithmetic circuit and ensures its generation to various representations.
The __init__ method fills some mandatory attributes concerning arithmetic circuit
that are later used for generation into various representations.
"""
def __init__(self, a, b, c, prefix: str, name: str, out_N: int, inner_component: bool = False, one_bit_circuit: bool = False, signed: bool = False, **kwargs):
super().__init__(prefix, name, out_N, inner_component, inputs=[a, b, c], signed=signed, **kwargs)
if one_bit_circuit is False:
if prefix == "":
self.prefix = name
else:
self.prefix = prefix + "_" + name
self.inner_component = inner_component
if self.inner_component is True:
self.a = Bus(prefix=f"{self.prefix}_{a.prefix}", wires_list=a.bus)
self.b = Bus(prefix=f"{self.prefix}_{b.prefix}", wires_list=b.bus)
self.c = Bus(prefix=f"{self.prefix}_{c.prefix}", wires_list=c.bus)
if a.is_output_bus():
self.a.connect_bus(connecting_bus=a)
if b.is_output_bus():
self.b.connect_bus(connecting_bus=b)
if c.is_output_bus():
self.c.connect_bus(connecting_bus=c)
else:
self.a = Bus(prefix=f"{a.prefix}", wires_list=a.bus)
self.b = Bus(prefix=f"{b.prefix}", wires_list=b.bus)
self.c = Bus(prefix=f"{c.prefix}", wires_list=c.bus)
""" C CODE GENERATION """
def get_prototype_c(self):
"""Generates C code function header to describe corresponding arithmetic circuit's interface in C code.
Returns:
str: Function's name and parameters in C code.
"""
return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.prefix}, {self.c_data_type} {self.b.prefix}, {self.c_data_type} {self.c.prefix})" + "{" + "\n"
def get_function_block_c(self):
"""Generates hierarchical C code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
Returns:
str: Hierarchical C code of multi-bit arithmetic circuit's function block description.
"""
# Obtain proper circuit name with its bit width
circuit_prefix = self.__class__(
a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
N=self.N, prefix="b"), c=Bus(
N=self.N, prefix="c"), name=circuit_prefix)
return f"{circuit_block.get_circuit_c()}\n\n"
def get_declaration_c_hier(self):
"""Generates hierarchical C code declaration of corresponding subcomponent input/output wires inside the upper component.
Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present
inside the upper component from which function block has been invoked.
Returns:
str: Hierarchical C code of subcomponent arithmetic circuit's wires declaration.
"""
return f" {self.c_data_type} {self.a.prefix} = 0;\n" + \
f" {self.c_data_type} {self.b.prefix} = 0;\n" + \
f" {self.c_data_type} {self.c.prefix} = 0;\n" + \
f" {self.c_data_type} {self.out.prefix} = 0;\n"
def get_out_invocation_c(self):
"""Generates hierarchical C code invocation of corresponding arithmetic circuit's generated function block.
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
Assigns output values from invocation of the corresponding function block into inner wires present inside
the upper component from which function block has been invoked.
Returns:
str: Hierarchical C code of subcomponent's C function invocation and output assignment.
"""
# Getting name of circuit type for proper C code generation without affecting actual generated composition
circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
return self.a.return_bus_wires_values_c_hier() + self.b.return_bus_wires_values_c_hier() + self.c.return_bus_wires_values_c_hier() + \
f" {self.out.prefix} = {circuit_type}({self.a.prefix}, {self.b.prefix}, {self.c.prefix});\n"
""" VERILOG CODE GENERATION """
def get_prototype_v(self):
"""Generates Verilog code module header to describe corresponding arithmetic circuit's interface in Verilog code.
Returns:
str: Module's name and parameters in Verilog code.
"""
return f"module {self.prefix}(input [{self.N-1}:0] {self.a.prefix}, input [{self.N-1}:0] {self.b.prefix}, input [{self.N-1}:0] {self.c.prefix}, output [{self.out.N-1}:0] {self.out.prefix});\n"
def get_function_block_v(self):
"""Generates hierarchical Verilog code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
Returns:
str: Hierarchical Verilog code of multi-bit arithmetic circuit's function block description.
"""
# Obtain proper circuit name with its bit width
circuit_prefix = self.__class__(
a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
N=self.N, prefix="b"), c=Bus(N=self.N, prefix="c"), name=circuit_prefix)
return f"{circuit_block.get_circuit_v()}\n\n"
def get_declaration_v_hier(self):
"""Generates hierarchical Verilog code declaration of corresponding subcomponent input/output wires inside the upper component.
Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present
inside the upper component from which function block has been invoked.
Returns:
str: Hierarchical Verilog code of subcomponent arithmetic circuit's wires declaration.
"""
return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \
f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \
f" wire [{self.c.N-1}:0] {self.c.prefix};\n" + \
f" wire [{self.out.N-1}:0] {self.out.prefix};\n"
def get_out_invocation_v(self):
"""Generates hierarchical Verilog code invocation of corresponding arithmetic circuit's generated function block.
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
Assigns output values from invocation of the corresponding function block into inner wires present inside
the upper component from which function block has been invoked.
Returns:
str: Hierarchical Verilog code of subcomponent's module invocation and output assignment.
"""
# Getting name of circuit type and insitu copying out bus for proper Verilog code generation without affecting actual generated composition
circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
N=self.N, prefix="b"), c=Bus(
N=self.N, prefix="c"), name=circuit_type)
return self.a.return_bus_wires_values_v_hier() + self.b.return_bus_wires_values_v_hier() + self.c.return_bus_wires_values_v_hier() + \
f" {circuit_type} {circuit_type}_{self.out.prefix}(.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.c.prefix}({self.c.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n"
""" BLIF CODE GENERATION """
def get_declaration_blif(self):
"""Generates flat Blif code declaration of input/output circuit wires.
Returns:
str: Flat Blif code containing declaration of circuit's wires.
"""
if self.N == 1:
return f".inputs {self.a.prefix} {self.b.prefix} {self.c.prefix}\n" + \
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
f".names vdd\n1\n" + \
f".names gnd\n0\n"
else:
return f".inputs{self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}{self.c.get_wire_declaration_blif()}\n" + \
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
f".names vdd\n1\n" + \
f".names gnd\n0\n"
def get_invocation_blif_hier(self):
"""Generates hierarchical Blif code invocation of corresponding arithmetic circuit's generated function block.
Used for multi-bit subcomponent's modul invocation.
Returns:
str: Hierarchical Blif code of subcomponent's model invocation and output assignment.
"""
# Getting name of circuit type for proper Blif code generation without affecting actual generated composition
circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
return f"{self.a.get_wire_assign_blif(output=True)}" + \
f"{self.b.get_wire_assign_blif(output=True)}" + \
f"{self.c.get_wire_assign_blif(output=True)}" + \
f".subckt {circuit_type}" + \
"".join([f" a[{self.a.bus.index(w)}]={self.a.prefix}[{self.a.bus.index(w)}]" for w in self.a.bus]) + \
"".join([f" b[{self.b.bus.index(w)}]={self.b.prefix}[{self.b.bus.index(w)}]" for w in self.b.bus]) + \
"".join([f" c[{self.c.bus.index(w)}]={self.c.prefix}[{self.c.bus.index(w)}]" for w in self.c.bus]) + \
"".join(
[f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n"
def get_function_block_blif(self):
"""Generates hierarchical Blif code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
Returns:
str: Hierarchical Blif code of multi-bit arithmetic circuit's function block description.
"""
# Obtain proper circuit name with its bit width
circuit_prefix = self.__class__(
a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
N=self.N, prefix="b"), c=Bus(N=self.N, prefix="c"), name=circuit_prefix)
return f"{circuit_block.get_circuit_blif()}"

View File

@ -9,6 +9,7 @@ from ariths_gen.wire_components import (
from io import StringIO
class GeneralCircuit():
"""Class represents a general circuit and ensures its generation to various representations.
@ -16,7 +17,7 @@ class GeneralCircuit():
that are later used for generation into various representations.
"""
def __init__(self, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list=[], signed: bool = False, outname = None):
def __init__(self, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list = [], signed: bool = False, outname=None):
if prefix == "":
self.prefix = name
else:
@ -32,7 +33,7 @@ class GeneralCircuit():
self.circuit_gates = []
self.c_data_type = "uint64_t"
self.signed = signed
self.pyc = None # Python compiled function
self.pyc = None # Python compiled function
def __call__(self, *args):
if not self.pyc:
@ -41,7 +42,6 @@ class GeneralCircuit():
globs, locs = {}, {}
exec(buf.getvalue(), globs, locs)
#print(locs)
self.pyc = locs[self.prefix]
return self.pyc(*args)
@ -208,6 +208,9 @@ class GeneralCircuit():
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.a.bus]
[self.circuit_wires.append(
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.b.bus]
if hasattr(self, 'c'):
[self.circuit_wires.append(
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.c.bus]
else:
self.circuit_wires.append(
(self.a, f"{self.a.name}", self.save_wire_id(wire=self.a)))
@ -397,18 +400,15 @@ class GeneralCircuit():
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])
return "".join([c.get_gate_invocation_c() if isinstance(c, TwoInputLogicGate) else c.get_out_invocation_c() for c in self.components])
def get_out_invocation_c(self, circuit_prefix: str):
def get_out_invocation_c(self):
"""Generates hierarchical C code invocation of corresponding arithmetic circuit's generated function block.
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.
"""
@ -549,18 +549,15 @@ class GeneralCircuit():
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])
return "".join([c.get_gate_invocation_v() if isinstance(c, TwoInputLogicGate) else c.get_out_invocation_v() for c in self.components])
def get_out_invocation_v(self, circuit_prefix: str):
def get_out_invocation_v(self):
"""Generates hierarchical Verilog code invocation of corresponding arithmetic circuit's generated function block.
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 Verilog code of subcomponent's module invocation and output assignment.
"""
@ -659,16 +656,13 @@ class GeneralCircuit():
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)
return "".join(c.get_invocation_blif_hier() for c in self.components)
def get_invocation_blif_hier(self, circuit_prefix: str):
def get_invocation_blif_hier(self):
"""Generates hierarchical Blif code invocation of corresponding arithmetic circuit's generated function block.
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.
"""
@ -741,7 +735,7 @@ class GeneralCircuit():
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}}"
return f"{{{sum(input_bus.N for input_bus in self.inputs)},{self.out.N},1,{len(self.circuit_gates)},2,1,0}}"
def get_triplets_cgp(self):
"""Generates list of logic gate triplets (first input wire, second input wire, logic gate function) using wires unique position indexes within the described circuit.

View File

@ -46,13 +46,13 @@ class MultiplierCircuit(ArithmeticCircuit):
"""
# To get the index of previous row's connecting adder and its generated pp
if mult_type == "bam":
#TODO alter to be more compact
# TODO alter to be more compact
ids_sum = 0
for row in range(self.horizontal_cut + self.ommited_rows, b_index):
first_row_elem_id = self.vertical_cut-row if self.vertical_cut-row > 0 else 0
# First pp row composed just from gates
if row == self.horizontal_cut + self.ommited_rows:
# Minus one because the first component has index 0 instead of 1
# Minus one because the first component has index 0 instead of 1
ids_sum += sum([1 for gate_pos in range(first_row_elem_id, self.N)])-1
elif row == b_index-1:
ids_sum += sum([2 for gate_adder_pos in range(first_row_elem_id, self.N) if gate_adder_pos <= a_index+1])
@ -65,8 +65,6 @@ class MultiplierCircuit(ArithmeticCircuit):
else:
index = ((b_index-2) * ((self.N)*2)) + ((self.N-1)+2*(a_index+2))
# Get carry wire as input for the last adder in current row
if a_index == self.N-1:
index = index-2
@ -103,26 +101,71 @@ class MultiplierCircuit(ArithmeticCircuit):
if d >= initial_value:
return stage, max_height
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.
def init_row_lengths(self):
"""Creates appropriate number of partial product rows along with filling them with corresponding number of bit pairs.
Returns:
list: List of partial product rows with their bit pairs.
"""
rows = [[] for _ in range(self.N)]
rows = [self.add_row_wires(row=row, row_index=rows.index(row)) for row in rows]
return rows
def add_row_wires(self, row: list, row_index: int):
"""Fills circuit's partial product row with corresponding bit pairs.
Args:
signed (bool, optional): Specify whether pp columns bit pairs should perform signed multiplication or not. Defaults to False.
row (list): List representing row of partial product bits.
row_index (int): Index of partial products row.
Returns:
list: Updated row list containing corresponding number of input bit pairs to form proper pp row.
"""
# Number of partial products present in the row (should be equal to circuit's input bus size)
row_pp_count = self.N
# Adding neccessary number of lists (based on number of bits in the row stored in `row_pp_count`)
# to row that each represent individual bit pairs for described row (these bit pairs are then combined in AND/NAND gates)
[row.append([]) for _ in range(row_pp_count)]
# Filling row bit pair lists with appropriate bits
[row[index].append(self.a.get_wire(index)) for index in range(row_pp_count)]
[row[index].append(self.b.get_wire(row_index)) for index in range(row_pp_count)]
# Converting unsigned rows of pp bit pair lists into AND gates
if self.signed is False:
row[0:] = [self.add_component(AndGate(a=row[i][0], b=row[i][1], prefix=self.prefix+'_and_'+str(row[i][0].index)+'_'+str(row[i][1].index), parent_component=self)).out for i in range(row_pp_count)]
# Converting signed rows of pp bit pair lists into AND/NAND gates (based on Baugh-Wooley multiplication algorithm)
else:
# Partial product bit pairs of all rows (expect for the last one) are connected to AND gates, besides the last pp bit pair in each row that is connected to a NAND gate
if row_index != self.N-1:
row[0:row_pp_count-1] = [self.add_component(AndGate(a=row[i][0], b=row[i][1], prefix=self.prefix+'_and_'+str(row[i][0].index)+'_'+str(row[i][1].index), parent_component=self)).out for i in range(row_pp_count-1)]
row[row_pp_count-1] = self.add_component(NandGate(a=row[row_pp_count-1][0], b=row[row_pp_count-1][1], prefix=self.prefix+'_nand_'+str(row[row_pp_count-1][0].index)+'_'+str(row[row_pp_count-1][1].index), parent_component=self)).out
# Partial product bit pairs of the last row are connected to NAND gates besides the last pp pair that is connected to an AND gate
else:
row[0:row_pp_count-1] = [self.add_component(NandGate(a=row[i][0], b=row[i][1], prefix=self.prefix+'_nand_'+str(row[i][0].index)+'_'+str(row[i][1].index), parent_component=self)).out for i in range(row_pp_count-1)]
row[row_pp_count-1] = self.add_component(AndGate(a=row[row_pp_count-1][0], b=row[row_pp_count-1][1], prefix=self.prefix+'_and_'+str(row[row_pp_count-1][0].index)+'_'+str(row[row_pp_count-1][1].index), parent_component=self)).out
pp_row_wires = Bus(prefix=f"pp_row{row_index}", wires_list=row)
return pp_row_wires
def init_column_heights(self):
"""Creates appropriate number of partial product columns along with filling them with corresponding number of bit pairs.
Returns:
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), signed=signed) for col in columns]
columns = [self.add_column_wires(column=col, column_index=columns.index(col)) for col in columns]
return columns
def add_column_wires(self, column: list, column_index: int, signed: bool):
def add_column_wires(self, column: list, column_index: int):
"""Fills circuit's partial product column with corresponding bit pairs.
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.
@ -139,7 +182,7 @@ class MultiplierCircuit(ArithmeticCircuit):
[column[index-(self.a.N-1-column[0])].append(self.b.get_wire(index)) for index in range(self.a.N-column[0], self.a.N)]
# Converting unsigned column pp bit pair lists into AND gates
if signed is False:
if self.signed is False:
column[1:] = [AndGate(a=column[i][0], b=column[i][1], prefix=self.prefix+'_and_'+str(column[i][0].index)+'_'+str(column[i][1].index), parent_component=self) for i in range(1, len(column))]
# Converting signed column pp bit pair lists into AND/NAND gates (based on Baugh-Wooley multiplication algorithm)
else:

View File

@ -17,3 +17,8 @@ from ariths_gen.multi_bit_circuits.adders.carry_skip_adder import (
UnsignedCarrySkipAdder,
SignedCarrySkipAdder
)
from ariths_gen.multi_bit_circuits.adders.carry_save_adder import (
CarrySaveAdderComponent,
UnsignedCarrySaveAdder
)

View File

@ -0,0 +1,155 @@
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit,
ThreeInputArithmeticCircuit,
MultiplierCircuit
)
from ariths_gen.one_bit_circuits.one_bit_components import (
HalfAdder,
PGLogicBlock,
FullAdder,
FullAdderPG
)
from ariths_gen.one_bit_circuits.logic_gates import (
AndGate,
NandGate,
OrGate,
NorGate,
XorGate,
XnorGate,
NotGate
)
from ariths_gen.multi_bit_circuits.adders import (
UnsignedCarryLookaheadAdder,
UnsignedPGRippleCarryAdder,
UnsignedRippleCarryAdder,
SignedCarryLookaheadAdder,
SignedPGRippleCarryAdder,
SignedRippleCarryAdder
)
class CarrySaveAdderComponent(ThreeInputArithmeticCircuit):
"""Class representing carry save adder component.
The carry save adder component is especially useful when constructing tree multiplier architectures to reduce the propagation delay as opposed to traditional implementation of tree multipliers with half/full adders.
The circuit is composed of full adders that operate paralelly and each take 3 input operands.
The final output bus is composed of sum and carry bits of its corresponding contained full adders.
```
C3 B3 A3 C2 B2 A2 C1 B1 A1 C0 B0 A0
FA FA FA FA
c3s3 c2s2 c1s1 c0s0
0 0/1
o9 o8 o7 o6 o5 o4 o3 o2 o1 o0
c3 c2 c1 c0 0 0/1 s3 s2 s1 s0
```
Description of the __init__ method.
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
c (Bus): Third input bus.
prefix (str, optional): Prefix name of csa component. Defaults to "".
name (str, optional): Name of csa component. Defaults to "csa_component".
signed (bool, optional): Specifies whether the component should be used in (un)signed context (used during the construction of tree multipliers). Defaults to False.
"""
def __init__(self, a: Bus, b: Bus, c: Bus, prefix: str = "", name: str = "csa_component", signed: bool = False, **kwargs):
self.N = max(a.N, b.N, c.N)
super().__init__(a=a, b=b, c=c, prefix=prefix, name=name, out_N=(2*self.N)+2, signed=signed, **kwargs)
bus_extension_wire = ConstantWireValue1() if self.signed is True else ConstantWireValue0()
self.a.bus_extend(N=self.N, prefix=a.prefix, desired_extension_wire=bus_extension_wire)
self.b.bus_extend(N=self.N, prefix=b.prefix, desired_extension_wire=bus_extension_wire)
self.c.bus_extend(N=self.N, prefix=c.prefix, desired_extension_wire=bus_extension_wire)
self.sum_bits = Bus(prefix=self.prefix + "_sums", N=int(self.out.N/2))
self.carry_bits = Bus(prefix=self.prefix + "_carries", N=int(self.out.N/2))
self.sum_bits.connect(-1, bus_extension_wire)
self.carry_bits.connect(0, ConstantWireValue0())
# Gradual addition of 1-bit CSA components (FAs are used)
for input_index in range(self.N):
obj_adder = FullAdder(self.a.get_wire(input_index), self.b.get_wire(input_index), self.c.get_wire(input_index), prefix=self.prefix+"_fa"+str(input_index))
self.add_component(obj_adder)
self.sum_bits.connect(input_index, obj_adder.get_sum_wire())
self.carry_bits.connect(input_index+1, obj_adder.get_carry_wire())
[self.out.connect(o, self.sum_bits.get_wire(o)) for o in range(0, int(self.out.N/2))]
[self.out.connect(o, self.carry_bits.get_wire(o-int(self.out.N/2))) for o in range(int(self.out.N/2), self.out.N)]
class UnsignedCarrySaveAdder(ThreeInputArithmeticCircuit):
"""Class representing unsigned carry save adder.
Unsigned carry save adder represents 3 input N-bit unsigned adder which is composed of
N one bit full adders, that are not interconnected as in for example the ripple carry adder,
but instead operate parallelly.
The sum and carry bits of the individual full adders are connected to a multi bit adder to sum the final result.
```
C3 B3 A3 C2 B2 A2 C1 B1 A1 C0 B0 A0
FA FA FA FA
c3s3c2s2c1s1c0s0
0 0
Y4X4 Y3X3 Y2X2 Y1X1 Y0X0
5-bit multi bit adder
Cout S4 S3 S2 S1 S0
```
Description of the __init__ method.
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
c (Bus): Third input bus.
prefix (str, optional): Prefix name of unsigned csa. Defaults to "".
name (str, optional): Name of unsigned csa. Defaults to "u_csa".
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sum. Defaults to UnsignedCarryLookaheadAdder.
"""
def __init__(self, a: Bus, b: Bus, c: Bus, prefix: str = "", name: str = "u_csa", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
self.N = max(a.N, b.N, c.N)
super().__init__(a=a, b=b, c=c, prefix=prefix, name=name, out_N=self.N+2, **kwargs)
# Bus sign extension in case buses have different lengths
self.a.bus_extend(N=self.N, prefix=a.prefix)
self.b.bus_extend(N=self.N, prefix=b.prefix)
self.c.bus_extend(N=self.N, prefix=c.prefix)
csa_adder = CarrySaveAdderComponent(a=self.a, b=self.b, c=self.c, prefix=prefix, name="csa_comp" + str(self.N), inner_component=True)
self.add_component(csa_adder)
# Obtain proper adder name with its bit width
adder_name = unsigned_adder_class_name(a=self.a, b=self.b).prefix + str(self.N+1)
adder_a = Bus(prefix="a", N=csa_adder.sum_bits.N)
adder_b = Bus(prefix="b", N=csa_adder.carry_bits.N)
adder_a.connect_bus(connecting_bus=csa_adder.out, end_connection_pos=int(csa_adder.out.N/2))
adder_b.connect_bus(connecting_bus=csa_adder.out, start_connection_pos=int(csa_adder.out.N/2), end_connection_pos=csa_adder.out.N, offset=int(csa_adder.out.N/2))
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True)
self.add_component(final_adder)
self.out.connect_bus(connecting_bus=final_adder.out)

View File

@ -1,4 +1,3 @@
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
@ -27,15 +26,13 @@ from ariths_gen.one_bit_circuits.logic_gates import (
XnorGate,
NotGate
)
import re
class UnsignedCGPCircuit(GeneralCircuit):
""" Circuit that loads CGP code and is able to export it to C/verilog/Blif/CGP """
"""Unsigned circuit variant that loads CGP code and is able to export it to C/verilog/Blif/CGP."""
def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs):
cgp_prefix, cgp_core, cgp_outputs = re.match(
r"{(.*)}(.*)\(([^()]+)\)", code).groups()
@ -43,25 +40,21 @@ class UnsignedCGPCircuit(GeneralCircuit):
int, cgp_prefix.split(","))
assert sum(
input_widths) == c_in, f"CGP input widht {c_in} doesn't match input_widhts {input_widths}"
#assert c_rows == 1, f"Only one-row CGP is supported {c_rows}x{c_cols}"
input_widths) == c_in, f"CGP input width {c_in} doesn't match input_widths {input_widths}"
inputs = [Bus(N=bw, prefix=f"input_{chr(i)}")
for i, bw in enumerate(input_widths, start=0x61)]
#vals = Bus(N=c_rows * c_cols, prefix=f"{prefix}_data")
# adding values to the list
# Adding values to the list
self.vals = {}
j = 2 # start from two, 0=false, 1 = true
j = 2 # Start from two, 0=False, 1=True
for iid, bw in enumerate(input_widths):
for i in range(bw):
assert j not in self.vals
self.vals[j] = inputs[iid].get_wire(i)
j += 1
super().__init__(prefix=prefix, name=name, out_N=c_out, inputs=inputs, **kwargs)
cgp_core = cgp_core.split(")(")
i = 0
@ -74,38 +67,37 @@ class UnsignedCGPCircuit(GeneralCircuit):
comp_set = dict(prefix=f"{self.prefix}_core_{i:03d}", parent_component=self)
a, b = self._get_wire(in_a), self._get_wire(in_b)
if fn == 0: # identity
if fn == 0: # IDENTITY
o = a
elif fn == 1: # not
elif fn == 1: # NOT
o = self.add_component(NotGate(a, **comp_set)).out
elif fn == 2: # and
elif fn == 2: # AND
o = self.add_component(AndGate(a, b, **comp_set)).out
elif fn == 3: # or
elif fn == 3: # OR
o = self.add_component(OrGate(a, b, **comp_set)).out
elif fn == 4: # xor
elif fn == 4: # XOR
o = self.add_component(XorGate(a, b, **comp_set)).out
elif fn == 5: # nand
elif fn == 5: # NAND
o = self.add_component(NandGate(a, b, **comp_set)).out
elif fn == 6: # nor
elif fn == 6: # NOR
o = self.add_component(NorGate(a, b, **comp_set)).out
elif fn == 7: # xnor
elif fn == 7: # XNOR
o = self.add_component(XnorGate(a, b, **comp_set)).out
elif fn == 8: # true
elif fn == 8: # TRUE
o = ConstantWireValue1()
elif fn == 9: # false
elif fn == 9: # FALSE
o = ConstantWireValue0()
assert i not in self.vals
self.vals[i] = o
# output connection
# Output connection
for i, o in enumerate(map(int, cgp_outputs.split(","))):
w = self._get_wire(o)
#print(i, o, w, w.name)
self.out.connect(i, w)
@staticmethod
def get_inputs_outputs(code : str):
def get_inputs_outputs(code: str):
cgp_prefix, cgp_core, cgp_outputs = re.match(
r"{(.*)}(.*)\(([^()]+)\)", code).groups()
@ -114,7 +106,6 @@ class UnsignedCGPCircuit(GeneralCircuit):
return c_in, c_out
def _get_wire(self, i):
if i == 0:
return ConstantWireValue0()
@ -122,11 +113,9 @@ class UnsignedCGPCircuit(GeneralCircuit):
return ConstantWireValue1()
return self.vals[i]
#self.mul = self.add_component(UnsignedArrayMultiplier(a=a, b=b, prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True))
#self.add = self.add_component(UnsignedRippleCarryAdder(a=r, b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True))
# self.out.connect_bus(connecting_bus=self.add.out)
class SignedCGPCircuit(UnsignedCGPCircuit):
"""Signed circuit variant that loads CGP code and is able to export it to C/verilog/Blif/CGP."""
def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs):
super().__init__(code=code, input_widths=input_widths, prefix=prefix, name=name, signed=True, **kwargs)
self.c_data_type = "int64_t"
self.c_data_type = "int64_t"

View File

@ -12,3 +12,8 @@ from ariths_gen.multi_bit_circuits.multipliers.dadda_multiplier import (
UnsignedDaddaMultiplier,
SignedDaddaMultiplier
)
from ariths_gen.multi_bit_circuits.multipliers.wallace_csa_multiplier import (
UnsignedWallaceCSAMultiplier,
SignedWallaceCSAMultiplier
)

View File

@ -165,7 +165,7 @@ class SignedDaddaMultiplier(MultiplierCircuit):
# 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(signed=True)
self.columns = self.init_column_heights()
# Not used for 1 bit multiplier
if self.N != 1:

View File

@ -0,0 +1,214 @@
from ariths_gen.wire_components import (
Wire,
ConstantWireValue0,
ConstantWireValue1,
Bus
)
from ariths_gen.core.arithmetic_circuits import (
ArithmeticCircuit,
MultiplierCircuit
)
from ariths_gen.one_bit_circuits.one_bit_components import (
HalfAdder,
FullAdder,
FullAdderPG
)
from ariths_gen.one_bit_circuits.logic_gates import (
AndGate,
NandGate,
OrGate,
NorGate,
XorGate,
XnorGate,
NotGate
)
from ariths_gen.multi_bit_circuits.adders import (
CarrySaveAdderComponent,
UnsignedCarryLookaheadAdder
)
class UnsignedWallaceCSAMultiplier(MultiplierCircuit):
"""Class representing unsigned wallace multiplier composed of carry save adder components.
Unsigned wallace multiplier represents fast N-bit multiplier which utilizes
the functionality of wallace tree reduction algorithm proposed by Chris Wallace.
First partial products are calculated for each bit pair that form the partial product multiplication rows.
This implementation uses carry save adder components to efficiently implement reduction of partial products utilizing the parallelism of the carry save adders.
At last the reduced pairs are inserted into chosen multi bit unsigned adder to execute their summation and obtain the final output bits.
Wallace tree algorithm is described more in detail here:
https://en.wikipedia.org/wiki/Wallace_tree
It presents a faster version of multiplier opposed to the conventional architectures that are composed of interconnected half/full adders.
```
PP7 PP6 PP5 PP4 PP3 PP2 PP1 PP0
CSA CSA
c1 s1 c0 s0
CSA CSA
c4 s4 c3 s3
CSA
c5 s5
CSA
c6 s6
CPA
o
```
Description of the __init__ method.
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
prefix (str, optional): Prefix name of unsigned csa wallace multiplier. Defaults to "".
name (str, optional): Name of unsigned csa wallace multiplier. Defaults to "u_wallaceCSA_cla".
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder.
"""
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_wallaceCSA_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
self.N = max(a.N, b.N)
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
# Bus sign extension in case buses have different lengths
self.a.bus_extend(N=self.N, prefix=a.prefix)
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Initialize all rows partial products forming AND gates matrix
self.rows = self.init_row_lengths()
# Zero extension of partial product rows
for i in range(0, len(self.rows)):
self.rows[i] = Bus(prefix=self.rows[i].prefix, wires_list=[ConstantWireValue0() for _ in range(0, i)] + self.rows[i].bus)
while len(self.rows) > 2:
# Gradual creation of unsigned csa adder components to reduce the pp rows to the total count of 2
pp_index = 0
while pp_index < len(self.rows) and (pp_index+2) < len(self.rows):
csa_reduction = CarrySaveAdderComponent(a=self.rows[pp_index], b=self.rows[pp_index+1], c=self.rows[pp_index+2], prefix=self.prefix+"_csa"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), inner_component=True)
self.add_component(csa_reduction)
# 3 pp rows have been reduced to 2
[self.rows.pop(pp_index) for i in range(3)]
# Append rows of sum and carry results from csa calculation
csa_sums_N = self.out.N if csa_reduction.sum_bits.N > self.out.N-1 else csa_reduction.sum_bits.N
csa_sums = Bus(prefix=self.prefix+"_csa_s"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), N=csa_sums_N)
csa_sums.connect_bus(connecting_bus=csa_reduction.out, end_connection_pos=csa_sums_N)
csa_carries_N = self.out.N if csa_reduction.carry_bits.N > self.out.N-1 else csa_reduction.carry_bits.N
csa_carries = Bus(prefix=self.prefix+"_csa_c"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), N=csa_carries_N)
csa_carries.connect_bus(connecting_bus=csa_reduction.out, start_connection_pos=int(csa_reduction.out.N/2), end_connection_pos=int(csa_reduction.out.N/2)+csa_carries.N, offset=int(csa_reduction.out.N/2))
self.rows.insert(pp_index, csa_carries)
self.rows.insert(pp_index, csa_sums)
# Update of the number of pp rows
pp_index += 2
# Final addition of remaining bits using chosen unsigned multi bit adder
# Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(self.rows[0].N)
adder_a = Bus(prefix="a", N=self.rows[0].N)
adder_b = Bus(prefix="b", N=self.rows[1].N)
[adder_a.connect(w, self.rows[0].get_wire(w)) for w in range(0, self.rows[0].N)]
[adder_b.connect(w, self.rows[1].get_wire(w)) for w in range(0, self.rows[1].N)]
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True)
self.add_component(final_adder)
[self.out.connect(o, final_adder.out.get_wire(o), inserted_wire_desired_index=o) for o in range(0, final_adder.out.N-1)]
class SignedWallaceCSAMultiplier(MultiplierCircuit):
"""Class representing signed wallace multiplier composed of carry save adder components.
Signed wallace multiplier represents fast N-bit multiplier which utilizes
the functionality of wallace tree reduction algorithm proposed by Chris Wallace and uses Baugh-Wooley algorithm
to perform signed multiplication.
First partial products are calculated for each bit pair that form the partial product multiplication rows.
This implementation uses carry save adder components to efficiently implement reduction of partial products utilizing the parallelism of the carry save adders.
At last the reduced pairs are inserted into chosen multi bit unsigned adder to execute their summation and obtain the final output bits, additional XOR gate serve the necessary sign extension.
Wallace tree algorithm is described more in detail here:
https://en.wikipedia.org/wiki/Wallace_tree
It presents a faster version of multiplier opposed to the conventional architectures that are composed of interconnected half/full adders.
Description of the __init__ method.
Args:
a (Bus): First input bus.
b (Bus): Second input bus.
prefix (str, optional): Prefix name of signed csa wallace multiplier. Defaults to "".
name (str, optional): Name of signed csa wallace multiplier. Defaults to "s_wallaceCSA_cla".
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder.
"""
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_wallaceCSA_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
self.N = max(a.N, b.N)
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
self.c_data_type = "int64_t"
# Bus sign extension in case buses have different lengths
self.a.bus_extend(N=self.N, prefix=a.prefix)
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Initialize all rows partial products forming AND gates matrix
self.rows = self.init_row_lengths()
# Zero extension of partial product rows
for i in range(0, len(self.rows)):
self.rows[i] = Bus(prefix=self.rows[i].prefix, wires_list=[ConstantWireValue0() for _ in range(0, i)] + self.rows[i].bus)
while len(self.rows) > 2:
# Gradual creation of signed csa adder components to reduce the pp rows to the total count of 2
pp_index = 0
while pp_index < len(self.rows) and (pp_index+2) < len(self.rows):
csa_reduction = CarrySaveAdderComponent(a=self.rows[pp_index], b=self.rows[pp_index+1], c=self.rows[pp_index+2], prefix=self.prefix+"_csa"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), inner_component=True, signed=True)
self.add_component(csa_reduction)
# 3 pp rows have been reduced to 2
[self.rows.pop(pp_index) for i in range(3)]
# Append rows of sum and carry results from csa calculation
csa_sums_N = self.out.N if csa_reduction.sum_bits.N > self.out.N-1 else csa_reduction.sum_bits.N
csa_sums = Bus(prefix=self.prefix+"_csa_s"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), N=csa_sums_N)
csa_sums.connect_bus(connecting_bus=csa_reduction.out, end_connection_pos=csa_sums_N)
csa_carries_N = self.out.N if csa_reduction.carry_bits.N > self.out.N-1 else csa_reduction.carry_bits.N
csa_carries = Bus(prefix=self.prefix+"_csa_c"+str(self.get_instance_num(cls=CarrySaveAdderComponent)), N=csa_carries_N)
csa_carries.connect_bus(connecting_bus=csa_reduction.out, start_connection_pos=int(csa_reduction.out.N/2), end_connection_pos=int(csa_reduction.out.N/2)+csa_carries.N, offset=int(csa_reduction.out.N/2))
self.rows.insert(pp_index, csa_carries)
self.rows.insert(pp_index, csa_sums)
# Update of the number of pp rows
pp_index += 2
# Final addition of remaining bits using chosen unsigned multi bit adder
# Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(self.rows[0].N)
adder_a = Bus(prefix="a", N=self.rows[0].N)
adder_b = Bus(prefix="b", N=self.rows[1].N)
[adder_a.connect(w, self.rows[0].get_wire(w)) for w in range(0, self.rows[0].N)]
[adder_b.connect(w, self.rows[1].get_wire(w)) for w in range(0, self.rows[1].N)]
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True)
self.add_component(final_adder)
[self.out.connect(o, final_adder.out.get_wire(o), inserted_wire_desired_index=o) for o in range(0, final_adder.out.N-1)]
# Final XOR to ensure proper sign extension
obj_xor = XorGate(ConstantWireValue1(), self.out.get_wire(self.out.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
self.add_component(obj_xor)
self.out.connect(self.out.N-1, obj_xor.out)

View File

@ -156,7 +156,7 @@ class SignedWallaceMultiplier(MultiplierCircuit):
self.b.bus_extend(N=self.N, prefix=b.prefix)
# Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication
self.columns = self.init_column_heights(signed=True)
self.columns = self.init_column_heights()
# Not used for 1 bit multiplier
if self.N != 1:

View File

@ -1,4 +1,4 @@
from .wires import Wire
from .wires import Wire, ConstantWireValue0, ConstantWireValue1
class Bus():
@ -24,7 +24,7 @@ class Bus():
self.prefix = prefix
self.bus = wires_list
self.N = len(self.bus)
# Determine C code signedness
self.signed = signed
if self.N > 8:
@ -48,23 +48,23 @@ class Bus():
"""
return self.out_bus
def bus_extend(self, N: int, prefix: str = "bus", last_wire_extend: bool = True):
def bus_extend(self, N: int, prefix: str = "bus", desired_extension_wire: Wire = ConstantWireValue0()):
"""Provides bus extension to contain more wires.
Args:
N (int): Number of wires in the bus. Defaults to 1.
prefix (str, optional): Prefix name of the bus. Defaults to "bus".
last_wire_extend (bool, optional): Specifies whether the last wire of the bus should be extended (connected) to all the extending wires. Defaults to True.
desired_extension_wire (Wire, optional): Specifies the wire that should be connected to all of the extending bus wires. Defaults to ConstantWireValue0().
"""
# Checks if any extension is neccesarry and if so, proceeds to wire extend the bus
if self.N < N:
# Adding wires into current bus's wires list (wire names are concatenated from bus prefix and their index position inside the bus in square brackets)
self.bus += [Wire(name=prefix+f"[{i}]", prefix=prefix, index=i, parent_bus=self) for i in range(self.N, N)]
if last_wire_extend is True:
for w_index in range(self.N, N):
self.connect(bus_wire_index=w_index, inner_component_out_wire=self.get_wire(self.N - 1))
for w_index in range(self.N, N):
self.connect(bus_wire_index=w_index, inner_component_out_wire=desired_extension_wire)
self.N = N
def get_wire(self, wire_index: int = 0):
"""Retrieves a wire from the bus by a given index.
@ -100,7 +100,7 @@ class Bus():
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)
def connect_bus(self, connecting_bus: object, start_connection_pos: int = 0, end_connection_pos: int = -1):
def connect_bus(self, connecting_bus: object, start_connection_pos: int = 0, end_connection_pos: int = -1, offset: int = 0):
"""Ensures connection of specified bus wires to this bus wires.
Used for connection of some inner circuit component's output bus (`connecting_bus`) wires
@ -110,11 +110,12 @@ class Bus():
connecting_bus (object): Specifies the connecting bus.
start_connection_pos (int, optional): Specifies the position from which to start interconnecting wires from the `connecting_bus` to this `self` bus. Defaults to 0.
end_connection_pos (int, optional): Specifies the position from which to end interconnecting wires from the `connecting_bus` to this `self` bus. Defaults to -1.
offset (int, optional): Specifies the offset wire index position in the `self` bus for proper connection (i.e. wire at index position 5 in the `connecting_bus` with offset set to 5 will be connected to `self` bus index position 0). Default to 0.
"""
if end_connection_pos == -1:
end_connection_pos = self.N
[self.connect(o, connecting_bus.get_wire(o), inserted_wire_desired_index=o) for o in range(start_connection_pos, end_connection_pos)]
[self.connect(o-offset, connecting_bus.get_wire(o), inserted_wire_desired_index=o) for o in range(start_connection_pos, end_connection_pos)]
""" PYTHON CODE GENERATION """
def return_bus_wires_values_python_flat(self):
@ -123,8 +124,11 @@ class Bus():
Returns:
str: Python code for assigning wire values into bus represented in Python code variable.
"""
return "".join([f" {self.prefix} = 0\n"] + [f" {self.prefix} |= {w.return_wire_value_python_flat(offset=self.bus.index(w))}" for w in self.bus])
# Ensures correct binding between the bus wire index and the wire itself
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
return "".join([f" {self.prefix} = 0\n"] + [f" {self.prefix} |= {w[1].return_wire_value_python_flat(offset=w[0])}" for w in mapped_positions])
def return_bus_wires_sign_extend_python(self):
"""Sign extends the bus's corresponding Python variable (object) to ensure proper Python code variable signedness.
@ -145,7 +149,6 @@ class Bus():
str: C code for declaration and initialization of bus name.
"""
return f" {self.c_type} {self.prefix} = 0;\n"
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.
@ -153,7 +156,10 @@ class Bus():
Returns:
str: C code for assigning wire values into bus represented in C code variable.
"""
return "".join([f" {self.prefix} |= {w.return_wire_value_c_flat(offset=self.bus.index(w))}" for w in self.bus])
# Ensures correct binding between the bus wire index and the wire itself
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
return "".join([f" {self.prefix} |= {w[1].return_wire_value_c_flat(offset=w[0])}" for w in mapped_positions])
def return_bus_wires_values_c_hier(self):
"""Retrieves values from bus's wires and stores them in bus's corresponding C variable at proper offset bit position in the bus for hierarchical generation.
@ -161,7 +167,10 @@ class Bus():
Returns:
str: C code for assigning wire values into bus represented in C code variable.
"""
return "".join([f" {self.prefix} |= {w.return_wire_value_c_hier(offset=self.bus.index(w))}" for w in self.bus])
# Ensures correct binding between the bus wire index and the wire itself
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
return "".join([f" {self.prefix} |= {w[1].return_wire_value_c_hier(offset=w[0])}" for w in mapped_positions])
def return_bus_wires_sign_extend_c(self):
"""Sign extends the bus's corresponding C variable to ensure proper C code variable signedness.
@ -182,7 +191,10 @@ class Bus():
Returns:
str: Verilog code for assigning wire values into bus represented in Verilog code bus variable.
"""
return "".join([f" assign {self.prefix}[{self.bus.index(w)}] = {w.return_wire_value_v_flat()}" for w in self.bus])
# Ensures correct binding between the bus wire index and the wire itself
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
return "".join([f" assign {self.prefix}[{w[0]}] = {w[1].return_wire_value_v_flat()}" for w in mapped_positions])
def return_bus_wires_values_v_hier(self):
"""Retrieves values from bus's wires and stores them in bus's corresponding Verilog variable at proper offset bit position in the bus for hierarchical generation.
@ -190,7 +202,10 @@ class Bus():
Returns:
str: Verilog code for assigning wire values into bus represented in Verilog code variable.
"""
return "".join([f" assign {self.prefix}[{self.bus.index(w)}] = {w.return_wire_value_v_hier()}" for w in self.bus])
# Ensures correct binding between the bus wire index and the wire itself
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
return "".join([f" assign {self.prefix}[{w[0]}] = {w[1].return_wire_value_v_hier()}" for w in mapped_positions])
def get_unique_assign_out_wires_v(self, circuit_block: object):
"""Returns bus's wires used for hierarchical one bit subcomponent's function block invocation and output wires assignments.
@ -215,7 +230,10 @@ class Bus():
Returns:
str: Blif code for declaration of individual bus wires.
"""
return "".join([f" {w.get_declaration_blif(prefix=self.prefix, offset=self.bus.index(w), array=array)}" for w in self.bus])
# Ensures correct binding between the bus wire index and the wire itself
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
return "".join([f" {w[1].get_declaration_blif(prefix=self.prefix, offset=w[0], array=array)}" for w in mapped_positions])
def get_wire_assign_blif(self, output: bool = False):
"""Assign all bits from the bus as each individual wires or assign wires into the corresponding output bus position in Blif code representation.
@ -226,7 +244,10 @@ class Bus():
Returns:
str: Blif code for bus wires assignments.
"""
return "".join([w.get_assign_blif(prefix=self.prefix+f"[{self.bus.index(w)}]", output=output) for w in self.bus])
# Ensures correct binding between the bus wire index and the wire itself
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
return "".join([w[1].get_assign_blif(prefix=self.prefix+f"[{w[0]}]", output=output) for w in mapped_positions])
def get_unique_assign_out_wires_blif(self, function_block_out_bus: object):
"""Assigns unique output wires to their respective outputs of subcomponent's function block modul in hierarchical Blif subcomponent's invocation.