diff --git a/b_asic/architecture.py b/b_asic/architecture.py index 97928ca123cc75444257a197e679bfa6af3f54ae..8daf5391936294cf0c6856662a70fcd59109508c 100644 --- a/b_asic/architecture.py +++ b/b_asic/architecture.py @@ -110,7 +110,7 @@ class HardwareBlock: @property def schedule_time(self) -> int: - """The schedule time for hardware block""" + """The schedule time for hardware block.""" raise NotImplementedError() def write_component_declaration(self, f: TextIOWrapper, indent: int = 1) -> None: @@ -236,6 +236,7 @@ class Resource(HardwareBlock): Parameters ---------- title : str, optional + Figure title. **kwargs Passed to :meth:`b_asic.resources.ProcessCollection.plot`. """ @@ -289,24 +290,26 @@ class Resource(HardwareBlock): """ Add a :class:`~b_asic.process.Process` to this :class:`Resource`. - Raises :class:`KeyError` if the process being added is not of the same type - as the other processes. - Parameters ---------- proc : :class:`~b_asic.process.Process` The process to add. assign : bool, default=False Whether to perform assignment of the resource after adding. + + Raises + ------ + :class:`TypeError` + If the process being added is not of the same type as the other processes. """ if isinstance(proc, OperatorProcess): # operation_type marks OperatorProcess associated operation. if not isinstance(proc._operation, self.operation_type): - raise KeyError(f"{proc} not of type {self.operation_type}") + raise TypeError(f"{proc} not of type {self.operation_type}") else: # operation_type is MemoryVariable or PlainMemoryVariable if not isinstance(proc, self.operation_type): - raise KeyError(f"{proc} not of type {self.operation_type}") + raise TypeError(f"{proc} not of type {self.operation_type}") self.collection.add_process(proc) if assign: self.assign() @@ -326,6 +329,11 @@ class Resource(HardwareBlock): The process to remove. assign : bool, default=False Whether to perform assignment of the resource after removal. + + Raises + ------ + :class:`KeyError` + If *proc* is not present in resource. """ self.collection.remove_process(proc) if assign: @@ -392,8 +400,8 @@ class ProcessingElement(Resource): heuristic : str, default: 'left_edge' The assignment algorithm. - * 'left_edge': Left-edge algorithm. - * 'graph_color': Graph-coloring based on exclusion graph. + * 'left_edge': Left-edge algorithm. + * 'graph_color': Graph-coloring based on exclusion graph. """ self._assignment = list( self._collection.split_on_execution_time(heuristic=heuristic) @@ -497,11 +505,11 @@ class Memory(Resource): The assignment algorithm. Depending on memory type the following are available: - * 'RAM' - * 'left_edge': Left-edge algorithm. - * 'graph_color': Graph-coloring based on exclusion graph. - * 'register' - * ... + * 'RAM' + * 'left_edge': Left-edge algorithm. + * 'graph_color': Graph-coloring based on exclusion graph. + * 'register' + * ... """ if self._memory_type == "RAM": self._assignment = self._collection.split_on_execution_time( @@ -736,17 +744,14 @@ of :class:`~b_asic.architecture.ProcessingElement` assign: bool = False, ): """ - Move a :class:`b_asic.process.Process` from one resource to another in the - architecture. + Move a :class:`b_asic.process.Process` from one resource to another. Both the resource moved from and will become unassigned after a process has been - moved. - - Raises :class:`KeyError` if ``proc`` is not present in resource ``re_from``. + moved, unless *assign* is set to True. Parameters ---------- - proc : :class:`b_asic.process.Process` or string + proc : :class:`b_asic.process.Process` or str The process (or its name) to move. re_from : :class:`b_asic.architecture.Resource` or str The resource (or its entity name) to move the process from. @@ -754,6 +759,11 @@ of :class:`~b_asic.architecture.ProcessingElement` The resource (or its entity name) to move the process to. assign : bool, default=False Whether to perform assignment of the resources after moving. + + Raises + ------ + :class:`KeyError` + If *proc* is not present in resource *re_from*. """ # Extract resources from name if isinstance(re_from, str): diff --git a/b_asic/resources.py b/b_asic/resources.py index 8fb2d6f275779053437bb59658a0b178c96e7ed4..fedad2d90c5a007cc9aa386ea6a7758baf9f7d94 100644 --- a/b_asic/resources.py +++ b/b_asic/resources.py @@ -700,7 +700,7 @@ class ProcessCollection: If True, the plot method allows plotting collections of variables with a greater lifetime than the schedule time. title : str, optional - Title of plot. + Figure title. """ fig, ax = plt.subplots() self.plot( diff --git a/b_asic/schedule.py b/b_asic/schedule.py index b41c3619952e6ebeda848c17349eb3e6bade8125..d6289cff221da2f1a0157790fd6a923d46183271 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -74,8 +74,10 @@ class Schedule: If the schedule is cyclic. algorithm : {'ASAP', 'ALAP', 'provided'}, default: 'ASAP' The scheduling algorithm to use. The following algorithm are available: - * ``'ASAP'``: As-soon-as-possible scheduling. - * ``'ALAP'``: As-late-as-possible scheduling. + + * ``'ASAP'``: As-soon-as-possible scheduling. + * ``'ALAP'``: As-late-as-possible scheduling. + If 'provided', use provided *start_times* and *laps* dictionaries. start_times : dict, optional Dictionary with GraphIDs as keys and start times as values. diff --git a/b_asic/sfg_generators.py b/b_asic/sfg_generators.py index 0b750a7e35364f0b784e0815420648510c4dd758..e912d10b683bea5bf11f4b8395f283f7afa15050 100644 --- a/b_asic/sfg_generators.py +++ b/b_asic/sfg_generators.py @@ -34,7 +34,7 @@ def wdf_allpass( Parameters ---------- coefficients : 1D-array - Coefficients to use for the allpass section + Coefficients to use for the allpass section. name : Name, optional The name of the SFG. If None, "WDF allpass section". @@ -141,7 +141,7 @@ def direct_form_fir( Parameters ---------- coefficients : 1D-array - Coefficients to use for the FIR filter section + Coefficients to use for the FIR filter section. name : Name, optional The name of the SFG. If None, "Direct-form FIR filter". mult_properties : dictionary, optional @@ -209,7 +209,7 @@ def transposed_direct_form_fir( Parameters ---------- coefficients : 1D-array - Coefficients to use for the FIR filter section + Coefficients to use for the FIR filter section. name : Name, optional The name of the SFG. If None, "Transposed direct-form FIR filter". mult_properties : dictionary, optional diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index d848d4ffc1955616c0f2afc397db99ca64ddb913..a9599e10bd669067ef14e542a1097b38f4485fad 100644 --- a/b_asic/signal_flow_graph.py +++ b/b_asic/signal_flow_graph.py @@ -363,9 +363,11 @@ class SFG(AbstractOperation): # Set the values of our input operations to the given input values. for op, arg in zip( self._input_operations, - self.quantize_inputs(input_values, bits_override) - if quantize - else input_values, + ( + self.quantize_inputs(input_values, bits_override) + if quantize + else input_values + ), ): op.value = arg @@ -399,7 +401,7 @@ class SFG(AbstractOperation): def connect_external_signals_to_components(self) -> bool: """ - Connects any external signals to the internal operations of SFG. + Connect any external signals to the internal operations of SFG. This SFG becomes unconnected to the SFG it is a component off, causing it to become invalid afterwards. Returns True if successful, @@ -441,16 +443,14 @@ class SFG(AbstractOperation): @property def input_operations(self) -> Sequence[Operation]: """ - Get the internal input operations in the same order as their respective input - ports. + Internal input operations in the same order as their respective input ports. """ return self._input_operations @property def output_operations(self) -> Sequence[Operation]: """ - Get the internal output operations in the same order as their respective output - ports. + Internal output operations in the same order as their respective output ports. """ return self._output_operations @@ -571,7 +571,7 @@ class SFG(AbstractOperation): Parameters ---------- name : Name - Name of the desired component(s) + Name of the desired component(s). """ return self._components_by_name.get(name, []) @@ -587,9 +587,9 @@ class SFG(AbstractOperation): Parameters ---------- name : Name - Name of the desired component(s) + Name of the desired component(s). output_index : int, default: 0 - The desired output index to get the result from + The desired output index to get the result from. """ keys = [] for comp in self.find_by_name(name): @@ -606,7 +606,7 @@ class SFG(AbstractOperation): Parameters ---------- component : Operation - The new operation(s), e.g. Multiplication + The new operation(s), e.g. Multiplication. graph_id : GraphID The GraphID to match the operation to replace. """ @@ -766,6 +766,8 @@ class SFG(AbstractOperation): def remove_operation(self, operation_id: GraphID) -> Union["SFG", None]: """ + Remove operation. + Returns a version of the SFG where the operation with the specified GraphID removed. @@ -813,10 +815,11 @@ class SFG(AbstractOperation): def get_precedence_list(self) -> Sequence[Sequence[OutputPort]]: """ - Returns a precedence list of the SFG where each element in n:th the - list consists of elements that are executed in the n:th step. If the - precedence list already has been calculated for the current SFG then - return the cached version. + Return a precedence list of the SFG. + + In the precedence list each element in n:th the list consists of elements that + are executed in the n:th step. If the precedence list already has been + calculated for the current SFG then return the cached version. """ if self._precedence_list: return self._precedence_list @@ -942,6 +945,7 @@ class SFG(AbstractOperation): def get_operations_topological_order(self) -> Iterable[Operation]: """ Return an Iterable of the Operations in the SFG in topological order. + Feedback loops makes an absolutely correct topological order impossible, so an approximate topological Order is returned in such cases in this implementation. @@ -1509,7 +1513,7 @@ class SFG(AbstractOperation): splines: str = "spline", ) -> None: """ - Shows a visual representation of the SFG using the default system viewer. + Display a visual representation of the SFG using the default system viewer. Parameters ---------- @@ -1575,7 +1579,7 @@ class SFG(AbstractOperation): Parameters ---------- factor : int - Number of times to unfold + Number of times to unfold. """ if factor == 0: diff --git a/test/test_architecture.py b/test/test_architecture.py index 729fe0343adcb97d868e1a4de3627428407be460..ef02ad0c1320072ba7bb1e0080faa8fcbabca7c8 100644 --- a/test/test_architecture.py +++ b/test/test_architecture.py @@ -33,13 +33,13 @@ def test_add_remove_process_from_resource(schedule_direct_form_iir_lp_filter: Sc operations.get_by_type_name(ConstantMultiplication.type_name()) ) for process in operations: - with pytest.raises(KeyError, match=f"{process} not of type"): + with pytest.raises(TypeError, match=f"{process} not of type"): memory.add_process(process) for process in mvs: - with pytest.raises(KeyError, match=f"{process} not of type"): + with pytest.raises(TypeError, match=f"{process} not of type"): pe.add_process(process) - with pytest.raises(KeyError, match="PlainMV not of type"): + with pytest.raises(TypeError, match="PlainMV not of type"): memory.add_process(PlainMemoryVariable(0, 0, {0: 2}, "PlainMV")) @@ -223,7 +223,7 @@ def test_move_process(schedule_direct_form_iir_lp_filter: Schedule): processing_elements[1].collection.from_name('add1') # Processes can only be moved when the source and destination process-types match - with pytest.raises(KeyError, match="cmul4.0 not of type"): + with pytest.raises(TypeError, match="cmul4.0 not of type"): architecture.move_process('cmul4.0', memories[0], processing_elements[0]) with pytest.raises(KeyError, match="invalid_name not in"): architecture.move_process('invalid_name', memories[0], processing_elements[1])