diff --git a/b_asic/GUI/arrow.py b/b_asic/GUI/arrow.py index 2fa24b15eca302ed6af7424b5010f775e4f75ed7..8cb05fecceb943e306fa24382ee2118f6e3d9d12 100644 --- a/b_asic/GUI/arrow.py +++ b/b_asic/GUI/arrow.py @@ -106,11 +106,26 @@ class Arrow(QGraphicsPathItem): return cast("InputPort", self._destination_port_button.port) def set_source_operation(self, source: "Operation"): - """Set operation of the source DragButton.""" + """ + Set operation of the source DragButton. + + Parameters + ---------- + source : :class:`~b_asic.operation.Operation` + The operation to use as source. + """ self._source_port_button._operation_button.operation = source def set_destination_operation(self, destination: "Operation"): - """Set operation of the destination DragButton.""" + """ + Set operation of the destination DragButton. + + Parameters + ---------- + destination : :class:`~b_asic.operation.Operation` + The operation to use as destination. + + """ self._destination_port_button._operation_button.operation = destination def remove(self): diff --git a/b_asic/GUI/precedence_graph_window.py b/b_asic/GUI/precedence_graph_window.py index 82f5ca5a88dd8e045347d8e63a9d112859f78879..090ca3d14a6f14cee679854fb4dff15203492af5 100644 --- a/b_asic/GUI/precedence_graph_window.py +++ b/b_asic/GUI/precedence_graph_window.py @@ -13,6 +13,8 @@ from qtpy.QtWidgets import ( class PrecedenceGraphWindow(QDialog): + """Precedence graph window.""" + pc = Signal() def __init__(self, window): diff --git a/b_asic/GUI/simulate_sfg_window.py b/b_asic/GUI/simulate_sfg_window.py index 671e9fe690940f54044b3c632f564c822fcc7cde..2e27170e4db751c57f52ec3ac31ca14e663fe61b 100644 --- a/b_asic/GUI/simulate_sfg_window.py +++ b/b_asic/GUI/simulate_sfg_window.py @@ -25,6 +25,8 @@ if TYPE_CHECKING: class SimulateSFGWindow(QDialog): + """Simulation window.""" + simulate = Signal() def __init__(self, window): diff --git a/b_asic/codegen/vhdl/architecture.py b/b_asic/codegen/vhdl/architecture.py index bc8d822780f1a1f8ea453769d3f4f37e0c336eca..67a0d04283b011fa3869f2003f249aceebba3364 100644 --- a/b_asic/codegen/vhdl/architecture.py +++ b/b_asic/codegen/vhdl/architecture.py @@ -33,6 +33,8 @@ def memory_based_storage( corresponds to the cell to assign all MemoryVariables in corresponding process collection. If unset, each MemoryVariable will be assigned to a unique cell. + entity_name : str + The entity name for the resulting HDL. word_length : int Word length of the memory variable objects. read_ports : int @@ -220,12 +222,13 @@ def memory_based_storage( mv = cast(PlainMemoryVariable, mv) write(f, 4, f'-- {mv!r}') for read_time in mv.reads.values(): + val = ( + mv.start_time + read_time - int(not (input_sync)) + ) % schedule_time write( f, 4, - 'when' - f' {(mv.start_time+read_time-int(not(input_sync))) % schedule_time}' - ' =>', + f'when {val} =>', ) write_lines( f, diff --git a/b_asic/codegen/vhdl/common.py b/b_asic/codegen/vhdl/common.py index abd7f1e3c373737d9103f766a032f005bb2d7a83..cae5c4f2b067ca9a1a0bfc9f590449e90fd19671 100644 --- a/b_asic/codegen/vhdl/common.py +++ b/b_asic/codegen/vhdl/common.py @@ -51,8 +51,9 @@ def ieee_header( numeric_std: bool = True, ): """ - Write the standard IEEE VHDL use header with includes of std_logic_1164 and - numeric_std. + Write the standard IEEE VHDL use header. + + This includes std_logic_1164 and numeric_std. Parameters ---------- @@ -81,7 +82,9 @@ def signal_declaration( quartus_ram_style: Optional[str] = None, ): """ - Create a VHDL signal declaration: :: + Create a VHDL signal declaration. + + The declaration looks like:: signal {name} : {type} [:= {default_value}]; @@ -136,7 +139,6 @@ def constant_declaration( signal_type: str, value: Any, name_pad: Optional[int] = None, - type_pad: Optional[int] = None, ): """ Write a VHDL constant declaration with a name, a type and a value. @@ -197,7 +199,7 @@ def process_prologue( Content of the process sensitivity list. indent : int, default: 1 Indentation level to use for this process. - name : Optional[str] + name : str, optional An optional name for the process. """ if name is not None: @@ -214,17 +216,19 @@ def process_epilogue( name: Optional[str] = None, ): """ + Write the epilogue of a regular VHDL process. + Parameters ---------- f : TextIO The TextIO object to write the type declaration to. - sensitivity_list : str + sensitivity_list : str, optional Content of the process sensitivity list. Not needed when writing the epilogue. indent : int, default: 1 Indentation level to use for this process. indent : int, default: 1 Indentation level to use for this process. - name : Optional[str] + name : str, optional An optional name of the ending process. """ _ = sensitivity_list @@ -256,7 +260,7 @@ def synchronous_process_prologue( Name of the clock. indent : int, default: 1 Indentation level to use for this process. - name : Optional[str] + name : str, optional An optional name for the process. """ process_prologue(f, sensitivity_list=clk, indent=indent, name=name) @@ -265,12 +269,12 @@ def synchronous_process_prologue( def synchronous_process_epilogue( f: TextIO, - clk: Optional[str], + clk: Optional[str] = None, indent: int = 1, name: Optional[str] = None, ): """ - Write only the epilogue of a regular VHDL synchronous process with a single clock. + Write the epilogue of a regular VHDL synchronous process with a single clock. The clock is the only item in the sensitivity list and is triggering a rising edge block by some body of VHDL code. @@ -279,12 +283,12 @@ def synchronous_process_epilogue( ---------- f : TextIO The TextIO to write the VHDL code onto. - clk : str + clk : str, optional Name of the clock. indent : int, default: 1 Indentation level to use for this process. - name : Optional[str] - An optional name for the process + name : str, optional + An optional name for the process. """ _ = clk write(f, indent + 1, 'end if;') @@ -314,8 +318,8 @@ def synchronous_process( Body of the `if rising_edge(clk) then` block. indent : int, default: 1 Indentation level to use for this process. - name : Optional[str] - An optional name for the process + name : str, optional + An optional name for the process. """ synchronous_process_prologue(f, clk, indent, name) for line in body.split('\n'): @@ -344,7 +348,7 @@ def synchronous_memory( A set of strings used as identifiers for the read ports of the memory. write_ports : Set[Tuple[str,str,str]] A set of strings used as identifiers for the write ports of the memory. - name : Optional[str] + name : str, optional An optional name for the memory process. """ assert len(read_ports) >= 1 @@ -391,7 +395,7 @@ def asynchronous_read_memory( A set of strings used as identifiers for the read ports of the memory. write_ports : Set[Tuple[str,str,str]] A set of strings used as identifiers for the write ports of the memory. - name : Optional[str] + name : str, optional An optional name for the memory process. """ assert len(read_ports) >= 1 diff --git a/b_asic/graph_component.py b/b_asic/graph_component.py index a91b84f95c1b8bbae4717789d2447ec073cf8266..1b3ac9a709a4875d8d832272abd153a107c39555 100644 --- a/b_asic/graph_component.py +++ b/b_asic/graph_component.py @@ -70,6 +70,7 @@ class GraphComponent(ABC): def param(self, name: str) -> Any: """ Get the value of a parameter. + Returns None if the parameter is not defined. """ raise NotImplementedError @@ -78,6 +79,7 @@ class GraphComponent(ABC): def set_param(self, name: str, value: Any) -> None: """ Set the value of a parameter. + Adds the parameter if it is not already defined. """ raise NotImplementedError @@ -85,8 +87,9 @@ class GraphComponent(ABC): @abstractmethod def copy(self, *args, **kwargs) -> "GraphComponent": """ - Get a new instance of this graph component type with the same name, id and - parameters. + Get a new instance of this graph component type. + + The new instance will have the same name, id, and parameters. """ raise NotImplementedError @@ -99,7 +102,9 @@ class GraphComponent(ABC): @abstractmethod def traverse(self) -> Generator["GraphComponent", None, None]: """ - Get a generator that recursively iterates through all components that + Get a generator for traversing the all connected components. + + The generator recursively iterates through all components that are connected to this operation, as well as the ones that they are connected to. """ diff --git a/b_asic/operation.py b/b_asic/operation.py index 1c31f7884e5e908981d02639854d1dc27f6270bf..28502c0876ea49181f1b34da76d76457bc9fa393 100644 --- a/b_asic/operation.py +++ b/b_asic/operation.py @@ -154,8 +154,7 @@ class Operation(GraphComponent, SignalSourceProvider): quantize: bool = True, ) -> Num: """ - Evaluate the output at the given index of this operation with the given input - values. + Evaluate the output at the given index with the given input values. Parameters ---------- diff --git a/b_asic/quantization.py b/b_asic/quantization.py index c1cc35960d6b85c961a61c0ffacb10fd77608ff2..26cc2c70a8ee138daf02055dab1710b94a408294 100644 --- a/b_asic/quantization.py +++ b/b_asic/quantization.py @@ -10,34 +10,22 @@ class Quantization(Enum): """Quantization types.""" ROUNDING = 1 - """ - Standard two's complement rounding, i.e, tie rounds towards infinity. - """ + """Standard two's complement rounding, i.e, tie rounds towards infinity.""" TRUNCATION = 2 - """ - Two's complement truncation, i.e., round towards negative infinity. - """ + """Two's complement truncation, i.e., round towards negative infinity.""" MAGNITUDE_TRUNCATION = 3 - """ - Magnitude truncation, i.e., round towards zero. - """ + """Magnitude truncation, i.e., round towards zero.""" JAMMING = 4 - """ - Jamming/von Neumann rounding, i.e., set the LSB to one. - """ + """Jamming/von Neumann rounding, i.e., set the LSB to one.""" UNBIASED_ROUNDING = 5 - """ - Unbiased rounding, i.e., tie rounds towards even. - """ + """Unbiased rounding, i.e., tie rounds towards even.""" UNBIASED_JAMMING = 6 - """ - Unbiased jamming/von Neumann rounding. - """ + """Unbiased jamming/von Neumann rounding.""" class Overflow(Enum): diff --git a/b_asic/resources.py b/b_asic/resources.py index 08668aecdf7ed889eadbcb79c80f56d8601dee21..25cc9f076d0feace936f089bb2c9757d228d1dc6 100644 --- a/b_asic/resources.py +++ b/b_asic/resources.py @@ -553,7 +553,7 @@ class ProcessCollection: Returns ------- ax : :class:`matplotlib.axes.Axes` - Associated Matplotlib Axes (or array of Axes) object + Associated Matplotlib Axes (or array of Axes) object. """ # Set up the Axes object @@ -1302,10 +1302,7 @@ class ProcessCollection: """ Generate VHDL code for register based storage. - This is based on Forward-Backward Register Allocation [1]. - - [1]: K. Parhi: VLSI Digital Signal Processing Systems: Design and - Implementation, Ch. 6.3.2 + This is based on Forward-Backward Register Allocation. Parameters ---------- @@ -1326,6 +1323,12 @@ class ProcessCollection: total_ports : int, default: 2 The total number of ports used when splitting process collection based on memory variable access. + + References + ---------- + - K. Parhi: VLSI Digital Signal Processing Systems: Design and + Implementation, Ch. 6.3.2 + """ # Check that entity name is a valid VHDL identifier if not is_valid_vhdl_identifier(entity_name): @@ -1372,8 +1375,7 @@ class ProcessCollection: def get_by_type_name(self, type_name: TypeName) -> "ProcessCollection": """ - Return a new :class:`~b_asic.resources.ProcessCollection` with only a given - type of operation. + Return a new ProcessCollection with only a given type of operation. Parameters ---------- diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 99ed184cd4a127c97e828ef8d816ea12bf5e1626..b342c022f8a4fd466d16c4f70d8e418ab64075b9 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -765,10 +765,10 @@ class Schedule: """ new_sfg = self._sfg() destination_laps = [] - for signal_id,lap in self._laps.items(): + for signal_id, lap in self._laps.items(): port = new_sfg.find_by_id(signal_id).destination destination_laps.append((port.operation.graph_id, port.index, lap)) - for op,port,lap in destination_laps: + for op, port, lap in destination_laps: for delays in range(lap): new_sfg = new_sfg.insert_operation_before(op, Delay(), port) return new_sfg() @@ -904,12 +904,11 @@ class Schedule: def get_memory_variables(self) -> ProcessCollection: """ - Return a :class:`~b_asic.resources.ProcessCollection` containing all - memory variables. + Return a ProcessCollection containing all memory variables. Returns ------- - ProcessCollection + :class:`~b_asic.resources.ProcessCollection` """ return ProcessCollection( set(self._get_memory_variables_list()), self.schedule_time @@ -917,12 +916,11 @@ class Schedule: def get_operations(self) -> ProcessCollection: """ - Return a :class:`~b_asic.resources.ProcessCollection` containing all - operations. + Return a ProcessCollection containing all operations. Returns ------- - ProcessCollection + :class:`~b_asic.resources.ProcessCollection` """ return ProcessCollection( { diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index 7d4610ae82d0756cef7da1c8b9daa75aeb8848c2..f33f36bd7be1680ffa854362a3654dcd10c766d5 100644 --- a/b_asic/signal_flow_graph.py +++ b/b_asic/signal_flow_graph.py @@ -741,7 +741,7 @@ class SFG(AbstractOperation): self, input_comp_id: GraphID, new_operation: Operation, - port: Optional[int] = None + port: Optional[int] = None, ) -> Optional["SFG"]: """ Insert an operation in the SFG before a given source operation. @@ -761,7 +761,8 @@ class SFG(AbstractOperation): new_operation : Operation The new operation, e.g. Multiplication. port : Optional[int] - The number of the InputPort before which the new operation shall be inserted + The number of the InputPort before which the new operation shall be + inserted. """ # Preserve the original SFG by creating a copy. @@ -1696,7 +1697,7 @@ class SFG(AbstractOperation): if factor == 0: raise ValueError("Unfolding 0 times removes the SFG") - sfg = self() # copy the sfg + sfg = self() # copy the sfg inputs = sfg.input_operations outputs = sfg.output_operations @@ -1706,9 +1707,8 @@ class SFG(AbstractOperation): for delay in sfg.find_by_type_name(Delay.type_name()): i = Input(name="input_" + delay.graph_id) o = Output( - src0=delay.input(0).signals[0].source, - name="output_" + delay.graph_id - ) + src0=delay.input(0).signals[0].source, name="output_" + delay.graph_id + ) inputs.append(i) outputs.append(o) @@ -1724,9 +1724,9 @@ class SFG(AbstractOperation): delay.input(0).signals[0].remove_source() delay.input(0).clear() - new_sfg = SFG(inputs, outputs) # The new sfg without the delays + new_sfg = SFG(inputs, outputs) # The new sfg without the delays - sfgs = [new_sfg() for _ in range(factor)] # Copy the SFG factor times + sfgs = [new_sfg() for _ in range(factor)] # Copy the SFG factor times # Add suffixes to all graphIDs and names in order to keep them separated for i in range(factor): @@ -1736,12 +1736,12 @@ class SFG(AbstractOperation): if operation.name[:7] not in ['', 'input_t', 'output_']: operation.name = operation.name + suffix - input_name_to_idx = {} # save the input port indices for future reference + input_name_to_idx = {} # save the input port indices for future reference new_inputs = [] # For each copy of the SFG, create new input operations for every "original" # input operation and connect them to begin creating the unfolded SFG for i in range(factor): - for port,operation in zip(sfgs[i].inputs, sfgs[i].input_operations): + for port, operation in zip(sfgs[i].inputs, sfgs[i].input_operations): if not operation.name.startswith("input_t"): i = Input() new_inputs.append(i) @@ -1758,22 +1758,24 @@ class SFG(AbstractOperation): new_outputs = [] delay_placements = {} for i in range(factor): - for port,operation in zip(sfgs[i].outputs, sfgs[i].output_operations): + for port, operation in zip(sfgs[i].outputs, sfgs[i].output_operations): if not operation.name.startswith("output_t"): new_outputs.append(Output(port)) else: - index = operation.name[8:] # Remove the "output_t" prefix + index = operation.name[8:] # Remove the "output_t" prefix j = (i + 1) % factor - number_of_delays_between = (i + 1)//factor + number_of_delays_between = (i + 1) // factor input_port = sfgs[j].input(input_name_to_idx["input_t" + index]) input_port.connect(port) - delay_placements[port] = [i,number_of_delays_between] - sfgs[i].graph_id = f'sfg{i}' # deterministically set the graphID of the sfgs + delay_placements[port] = [i, number_of_delays_between] + sfgs[i].graph_id = ( + f'sfg{i}' # deterministically set the graphID of the sfgs + ) - sfg = SFG(new_inputs, new_outputs) # create a new SFG to remove floating nodes + sfg = SFG(new_inputs, new_outputs) # create a new SFG to remove floating nodes # Insert the interconnect delays according to what is saved in delay_placements - for port,val in delay_placements.items(): + for port, val in delay_placements.items(): i, no_of_delays = val for _ in range(no_of_delays): sfg = sfg.insert_operation_after(f'sfg{i}.{port.index}', Delay()) @@ -1785,7 +1787,6 @@ class SFG(AbstractOperation): return sfg - @property def is_linear(self) -> bool: return all(op.is_linear for op in self.split())