diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py index be3892292d7ef786184445c90259d70c3232f62c..4b79eeed1436060a64851da5b15b9d9a74f71f1a 100644 --- a/b_asic/core_operations.py +++ b/b_asic/core_operations.py @@ -426,7 +426,8 @@ class ConstantMultiplication(AbstractOperation): latency: Optional[int] = None, latency_offsets: Optional[Dict[str, int]] = None, ): - """Construct a ConstantMultiplication operation with the given value.""" + """Construct a ConstantMultiplication operation with the given value. + """ super().__init__( input_count=1, output_count=1, diff --git a/b_asic/graph_component.py b/b_asic/graph_component.py index ac62222010e14582cc20d3486fd6eceec4a78104..624a42f3e68cab99d3be91c2156ced60680034dd 100644 --- a/b_asic/graph_component.py +++ b/b_asic/graph_component.py @@ -80,7 +80,8 @@ class GraphComponent(ABC): @abstractmethod def copy_component(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 with the same name, id and parameters. + """ raise NotImplementedError @property diff --git a/b_asic/operation.py b/b_asic/operation.py index 4b06c1b5d40ce59d54e3367d291ba6f181741990..33c5b753b8feb3f4b7854e9b537f5afea2038e94 100644 --- a/b_asic/operation.py +++ b/b_asic/operation.py @@ -251,12 +251,14 @@ class Operation(GraphComponent, SignalSourceProvider): @abstractmethod def inputs_required_for_output(self, output_index: int) -> Iterable[int]: - """Get the input indices of all inputs in this operation whose values are required in order to evaluate the output at the given output index.""" + """Get the input indices of all inputs in this operation whose values are required in order to evaluate the output at the given output index. + """ raise NotImplementedError @abstractmethod def truncate_input(self, index: int, value: Number, bits: int) -> Number: - """Truncate the value to be used as input at the given index to a certain bit length.""" + """Truncate the value to be used as input at the given index to a certain bit length. + """ raise NotImplementedError @property @@ -350,7 +352,7 @@ class AbstractOperation(Operation, AbstractGraphComponent): ] = None, latency: Optional[int] = None, latency_offsets: Optional[Dict[str, int]] = None, - execution_time: Optional[int] = None + execution_time: Optional[int] = None, ): """Construct an operation with the given input/output count. @@ -399,7 +401,8 @@ class AbstractOperation(Operation, AbstractGraphComponent): @abstractmethod def evaluate(self, *inputs) -> Any: # pylint: disable=arguments-differ - """Evaluate the operation and generate a list of output values given a list of input values.""" + """Evaluate the operation and generate a list of output values given a list of input values. + """ raise NotImplementedError def __add__(self, src: Union[SignalSourceProvider, Number]) -> "Addition": @@ -743,7 +746,8 @@ class AbstractOperation(Operation, AbstractGraphComponent): @property def preceding_operations(self) -> Iterable[Operation]: - """Returns an Iterable of all Operations that are connected to this Operations input ports.""" + """Returns an Iterable of all Operations that are connected to this Operations input ports. + """ return [ signal.source.operation for signal in self.input_signals @@ -752,7 +756,8 @@ class AbstractOperation(Operation, AbstractGraphComponent): @property def subsequent_operations(self) -> Iterable[Operation]: - """Returns an Iterable of all Operations that are connected to this Operations output ports.""" + """Returns an Iterable of all Operations that are connected to this Operations output ports. + """ return [ signal.destination.operation for signal in self.output_signals @@ -777,7 +782,8 @@ class AbstractOperation(Operation, AbstractGraphComponent): input_values: Sequence[Number], bits_override: Optional[int] = None, ) -> Sequence[Number]: - """Truncate the values to be used as inputs to the bit lengths specified by the respective signals connected to each input.""" + """Truncate the values to be used as inputs to the bit lengths specified by the respective signals connected to each input. + """ args = [] for i, input_port in enumerate(self.inputs): value = input_values[i] diff --git a/b_asic/port.py b/b_asic/port.py index 780dea502ba92bd67ee78fe8646a09f5676c0195..ab164d7547775370a2356c97e4ad2fca391c2c92 100644 --- a/b_asic/port.py +++ b/b_asic/port.py @@ -47,7 +47,8 @@ class Port(ABC): @latency_offset.setter @abstractmethod def latency_offset(self, latency_offset: int) -> None: - """Set the latency_offset of the port to the integer specified value.""" + """Set the latency_offset of the port to the integer specified value. + """ raise NotImplementedError @property diff --git a/b_asic/process.py b/b_asic/process.py index 2012a67ea26a4522ee834e2bdeb987f5c494ae46..53ccf882109cfde104a4814130557a1139fef1bf 100644 --- a/b_asic/process.py +++ b/b_asic/process.py @@ -1,42 +1,120 @@ -from typing import Dict, Int, List +from typing import Dict, Tuple from b_asic.operation import AbstractOperation from b_asic.port import InputPort, OutputPort class Process: - def __init__(self, start_time : Int, execution_time : Int): + """ + Object for use in resource allocation. Has a start time and an execution + time. Subclasses will in many cases contain additional information for + resource assignment. + """ + + def __init__(self, start_time: int, execution_time: int): + """ + Parameters + ---------- + start_time : int + Start time of process. + execution_time : int + Execution time (lifetime) of process. + """ self._start_time = start_time self._execution_time = execution_time def __lt__(self, other): return self._start_time < other.start_time or ( - self._start_time == other.start_time and self.execution_time > _execution_time) + self._start_time == other.start_time + and self.execution_time > other.execution_time + ) @property - def start_time(self) -> Int: + def start_time(self) -> int: + """Return the start time.""" return self._start_time @property - def execution_time(self) -> Int: + def execution_time(self) -> int: + """Return the execution time.""" return self._execution_time class OperatorProcess(Process): - def __init__(self, start_time : Int, operation : AbstractOperation): + """ + Object that corresponds to usage of an operator. + """ + + def __init__(self, start_time: int, operation: AbstractOperation): + """ + + Returns + ------- + object + """ super().__init__(start_time, operation.execution_time) self._operation = operation class MemoryVariable(Process): - def __init__(self, write_time : Int, write_port : OutputPort, reads : Dict[InputPort, Int]): - self._read_ports, self._life_times = reads.values() - super().__init__(start_time=write_time, execution_time=max(self._life_times)) + """ + Object that corresponds to a memory variable. + """ + + def __init__( + self, + write_time: int, + write_port: OutputPort, + reads: Dict[InputPort, int], + ): + self._read_ports = tuple(reads.keys()) + self._life_times = tuple(reads.values()) + self._write_port = write_port + super().__init__( + start_time=write_time, execution_time=max(self._life_times) + ) @property - def life_times(self) -> List[Int]: + def life_times(self) -> Tuple[int]: return self._life_times @property - def read_ports(self) -> List[InputPort]: + def read_ports(self) -> Tuple[InputPort]: return self._read_ports + + @property + def write_port(self) -> OutputPort: + return self._write_port + + +class PlainMemoryVariable(Process): + """ + Object that corresponds to a memory variable which only use numbers for + ports. This can be useful when only a plain memory variable is wanted with + no connection to a schedule. + """ + + def __init__( + self, + write_time: int, + write_port: int, + reads: Dict[int, int], + ): + self._read_ports = tuple(reads.keys()) + self._life_times = tuple(reads.values()) + self._write_port = write_port + super().__init__( + start_time=write_time, execution_time=max(self._life_times) + ) + + @property + def life_times(self) -> Tuple[int]: + return self._life_times + + @property + def read_ports(self) -> Tuple[int]: + return self._read_ports + + @property + def write_port(self) -> int: + return self._write_port diff --git a/b_asic/save_load_structure.py b/b_asic/save_load_structure.py index 3c8316c34495fc62c10869653a102e96912aaec4..f85bcfb473b8f07390c928afb3cf64da0567c7d8 100644 --- a/b_asic/save_load_structure.py +++ b/b_asic/save_load_structure.py @@ -110,7 +110,8 @@ def sfg_to_python(sfg: SFG, counter: int = 0, suffix: str = None) -> str: def python_to_sfg(path: str) -> SFG: - """Given a serialized file try to deserialize it and load it to the library.""" + """Given a serialized file try to deserialize it and load it to the library. + """ with open(path) as f: code = compile(f.read(), path, "exec") exec(code, globals(), locals()) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 3fceb49b62297a287330f802d847a706e14bb1e8..1a01d7453c9694d17782bbcc8cab3c0df1ad1616 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -64,7 +64,8 @@ class Schedule: self._schedule_time = schedule_time def start_time_of_operation(self, op_id: GraphID) -> int: - """Get the start time of the operation with the specified by the op_id.""" + """Get the start time of the operation with the specified by the op_id. + """ assert ( op_id in self._start_times ), "No operation with the specified op_id in this schedule." diff --git a/b_asic/scheduler_gui/__init__.py b/b_asic/scheduler_gui/__init__.py index a7edc03010c9cc8b0a0c3fa6e157337092132610..e2cb7dd6e6898d2af7ff92fa8550546152f3eb1f 100644 --- a/b_asic/scheduler_gui/__init__.py +++ b/b_asic/scheduler_gui/__init__.py @@ -3,7 +3,7 @@ Graphical user interface for B-ASIC scheduler. """ -__author__ = 'Andreas Bolin' +__author__ = "Andreas Bolin" # __all__ = ['main_window', 'graphics_graph', 'component_item', 'graphics_axes', 'graphics_timeline_item'] from b_asic.scheduler_gui._version import * diff --git a/b_asic/scheduler_gui/_version.py b/b_asic/scheduler_gui/_version.py index b1661f1fa26424000934d238345ea6dc9c9bca36..0ef1deb513ce5da0942c04df423c3f9950f30251 100644 --- a/b_asic/scheduler_gui/_version.py +++ b/b_asic/scheduler_gui/_version.py @@ -1,2 +1,2 @@ -__version_info__ = ('0', '1') -__version__ = '.'.join(__version_info__) +__version_info__ = ("0", "1") +__version__ = ".".join(__version_info__) diff --git a/b_asic/scheduler_gui/compile.py b/b_asic/scheduler_gui/compile.py index 29fe65767367193da8732a71ea1c5cf7bad68e3b..957f5d44f1c543ec3cb577590fee14ad0c8e5f17 100644 --- a/b_asic/scheduler_gui/compile.py +++ b/b_asic/scheduler_gui/compile.py @@ -17,11 +17,13 @@ from setuptools_scm import get_version try: import b_asic.scheduler_gui.logger as logger + log = logger.getLogger() sys.excepthook = logger.handle_exceptions except ModuleNotFoundError: log = None + def _check_filenames(*filenames: str) -> None: """Check if the filename(s) exist, otherwise raise FileNotFoundError exception.""" @@ -33,75 +35,78 @@ def _check_filenames(*filenames: str) -> None: def _check_qt_version() -> None: """Check if PySide2 or PyQt5 is installed, otherwise raise AssertionError exception.""" - assert uic.PYSIDE2 or uic.PYQT5, 'PySide2 or PyQt5 need to be installed' + assert uic.PYSIDE2 or uic.PYQT5, "PySide2 or PyQt5 need to be installed" def replace_qt_bindings(filename: str) -> None: """Raplaces qt-binding api in 'filename' from PySide2/PyQt5 to qtpy.""" - with open(f'{filename}', 'r') as file: + with open(f"{filename}", "r") as file: filedata = file.read() - filedata = filedata.replace('from PyQt5', 'from qtpy') - filedata = filedata.replace('from PySide2', 'from qtpy') - with open(f'{filename}', 'w') as file: + filedata = filedata.replace("from PyQt5", "from qtpy") + filedata = filedata.replace("from PySide2", "from qtpy") + with open(f"{filename}", "w") as file: file.write(filedata) def compile_rc(*filenames: str) -> None: """Compile resource file(s) given by 'filenames'. If no arguments are given, - the compiler will search for resource (.qrc) files and compile accordingly.""" + the compiler will search for resource (.qrc) files and compile accordingly. + """ _check_qt_version() def compile(filename: str = None) -> None: - outfile = f'{os.path.splitext(filename)[0]}_rc.py' - rcc = shutil.which('pyside2-rcc') - args = f'-g python -o {outfile} {filename}' + outfile = f"{os.path.splitext(filename)[0]}_rc.py" + rcc = shutil.which("pyside2-rcc") + args = f"-g python -o {outfile} {filename}" if rcc is None: - rcc = shutil.which('rcc') + rcc = shutil.which("rcc") if rcc is None: - rcc = shutil.which('pyrcc5') - args = f'-o {outfile} {filename}' + rcc = shutil.which("pyrcc5") + args = f"-o {outfile} {filename}" assert rcc, "PySide2 compiler failed, can't find rcc" os_ = sys.platform if os_.startswith("linux"): # Linux - cmd = f'{rcc} {args}' + cmd = f"{rcc} {args}" subprocess.call(cmd.split()) elif os_.startswith("win32"): # Windows # TODO: implement if log is not None: - log.error('Windows RC compiler not implemented') + log.error("Windows RC compiler not implemented") else: - print('Windows RC compiler not implemented') + print("Windows RC compiler not implemented") raise NotImplementedError elif os_.startswith("darwin"): # macOS # TODO: implement if log is not None: - log.error('macOS RC compiler not implemented') + log.error("macOS RC compiler not implemented") else: - print('macOS RC compiler not implemented') + print("macOS RC compiler not implemented") raise NotImplementedError else: # other OS if log is not None: - log.error(f'{os_} RC compiler not supported') + log.error(f"{os_} RC compiler not supported") else: - print(f'{os_} RC compiler not supported') + print(f"{os_} RC compiler not supported") raise NotImplementedError replace_qt_bindings(outfile) # replace qt-bindings with qtpy if not filenames: - rc_files = [os.path.join(root, name) - for root, _, files in os.walk('.') - for name in files - if name.endswith(('.qrc'))] - + rc_files = [ + os.path.join(root, name) + for root, _, files in os.walk(".") + for name in files + if name.endswith(".qrc") + ] + for filename in rc_files: compile(filename) - + else: _check_filenames(*filenames) for filename in filenames: @@ -116,51 +121,54 @@ def compile_ui(*filenames: str) -> None: def compile(filename: str) -> None: dir, file = os.path.split(filename) file = os.path.splitext(file)[0] - dir = dir if dir else '.' - outfile = f'{dir}/ui_{file}.py' + dir = dir if dir else "." + outfile = f"{dir}/ui_{file}.py" if uic.PYSIDE2: - uic_ = shutil.which('pyside2-uic') - args = f'-g python -o {outfile} {filename}' + uic_ = shutil.which("pyside2-uic") + args = f"-g python -o {outfile} {filename}" if uic_ is None: - uic_ = shutil.which('uic') + uic_ = shutil.which("uic") if uic_ is None: - uic_ = shutil.which('pyuic5') - args = f'-o {outfile} {filename}' + uic_ = shutil.which("pyuic5") + args = f"-o {outfile} {filename}" assert uic_, "PySide2 compiler failed, can't find uic" os_ = sys.platform if os_.startswith("linux"): # Linux - cmd = f'{uic_} {args}' + cmd = f"{uic_} {args}" subprocess.call(cmd.split()) elif os_.startswith("win32"): # Windows # TODO: implement - log.error('Windows UI compiler not implemented') + log.error("Windows UI compiler not implemented") raise NotImplementedError elif os_.startswith("darwin"): # macOS # TODO: implement - log.error('macOS UI compiler not implemented') + log.error("macOS UI compiler not implemented") raise NotImplementedError else: # other OS - log.error(f'{os_} UI compiler not supported') + log.error(f"{os_} UI compiler not supported") raise NotImplementedError - else: # uic.PYQT5 + else: # uic.PYQT5 from qtpy.uic import compileUi - with open(outfile, 'w') as ofile: + + with open(outfile, "w") as ofile: compileUi(filename, ofile) replace_qt_bindings(outfile) # replace qt-bindings with qtpy if not filenames: - ui_files = [os.path.join(root, name) - for root, _, files in os.walk('.') - for name in files - if name.endswith(('.ui'))] + ui_files = [ + os.path.join(root, name) + for root, _, files in os.walk(".") + for name in files + if name.endswith(".ui") + ] for filename in ui_files: compile(filename) else: @@ -176,43 +184,54 @@ def compile_all(): compile_ui() -if __name__ == '__main__': - parser = argparse.ArgumentParser(description=f'{__doc__}', - formatter_class=argparse.RawTextHelpFormatter) - +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=f"{__doc__}", formatter_class=argparse.RawTextHelpFormatter + ) - version = get_version(root='../..', relative_to=__file__) + version = get_version(root="../..", relative_to=__file__) - parser.add_argument('-v', '--version', - action='version', - version=f'%(prog)s v{version}') + parser.add_argument( + "-v", "--version", action="version", version=f"%(prog)s v{version}" + ) if sys.version_info >= (3, 8): - parser.add_argument('--ui', - metavar='<file>', - action='extend', - nargs='*', - help="compile form file(s) if <file> is given, otherwise search\n" - "for all form (*.ui) files and compile them all (default)") - parser.add_argument('--rc', - metavar='<file>', - action='extend', - nargs='*', - help="compile resource file(s) if <file> is given, otherwise\n" - "search for all resource (*.ui) files and compile them all") + parser.add_argument( + "--ui", + metavar="<file>", + action="extend", + nargs="*", + help=( + "compile form file(s) if <file> is given, otherwise search\n" + "for all form (*.ui) files and compile them all (default)" + ), + ) + parser.add_argument( + "--rc", + metavar="<file>", + action="extend", + nargs="*", + help=( + "compile resource file(s) if <file> is given, otherwise\n" + "search for all resource (*.ui) files and compile them all" + ), + ) else: - parser.add_argument('--ui', - metavar='<file>', - action='append', - help="compile form file") - parser.add_argument('--rc', - metavar='<file>', - action='append', - help="compile resource file") - - parser.add_argument('--all', - action='store_true', - help="search and compile all resource and form file(s)") + parser.add_argument( + "--ui", metavar="<file>", action="append", help="compile form file" + ) + parser.add_argument( + "--rc", + metavar="<file>", + action="append", + help="compile resource file", + ) + + parser.add_argument( + "--all", + action="store_true", + help="search and compile all resource and form file(s)", + ) if len(sys.argv) == 1: compile_ui() diff --git a/b_asic/scheduler_gui/graphics_axes_item.py b/b_asic/scheduler_gui/graphics_axes_item.py index 6a7da2ed6881fa2cb9b5f7ec3f4a6f391308890b..de660c26b6728c724779f8f4f35d35522c52349b 100644 --- a/b_asic/scheduler_gui/graphics_axes_item.py +++ b/b_asic/scheduler_gui/graphics_axes_item.py @@ -265,7 +265,6 @@ class GraphicsAxesItem(QGraphicsItemGroup): self._x_scale[index + 1].setX(self._x_scale[index + 1].x() + 1) def _make_base(self) -> None: - # x axis self._x_axis.setLine(0, 0, self._width_indent + self._width_padding, 0) self._x_axis.setPen(self._base_pen) diff --git a/b_asic/scheduler_gui/graphics_component_item.py b/b_asic/scheduler_gui/graphics_component_item.py index b78a3c3774d852bfb9aa2504914a13395d772b0a..d69df550600c2118ca1441e5ef738e05498efdf8 100644 --- a/b_asic/scheduler_gui/graphics_component_item.py +++ b/b_asic/scheduler_gui/graphics_component_item.py @@ -49,7 +49,8 @@ class GraphicsComponentItem(QGraphicsItemGroup): height: float = 0.75, parent: Optional[QGraphicsItem] = None, ): - """Constructs a GraphicsComponentItem. 'parent' is passed to QGraphicsItemGroup's constructor.""" + """Constructs a GraphicsComponentItem. 'parent' is passed to QGraphicsItemGroup's constructor. + """ super().__init__(parent=parent) self._operation = operation self._height = height @@ -155,7 +156,8 @@ class GraphicsComponentItem(QGraphicsItemGroup): ## component path def draw_component_path(keys: List[str], reversed: bool) -> None: - """Draws component path and also register port positions in self._ports dictionary.""" + """Draws component path and also register port positions in self._ports dictionary. + """ nonlocal x nonlocal y nonlocal old_x diff --git a/b_asic/scheduler_gui/graphics_graph_event.py b/b_asic/scheduler_gui/graphics_graph_event.py index c2a487050f08a2ae6d9f87b52f1c4fa0eeb7f65f..2cd0f8acf8a2337677d7b9fbaa76fe9acfc960c6 100644 --- a/b_asic/scheduler_gui/graphics_graph_event.py +++ b/b_asic/scheduler_gui/graphics_graph_event.py @@ -214,7 +214,8 @@ class GraphicsGraphEvent: # PyQt5 """Changes the cursor to ClosedHandCursor when grabbing an object and stores the current position in item's parent coordinates. 'event' will by default be accepted, and this item is then the mouse grabber. This - allows the item to receive future move, release and double-click events.""" + allows the item to receive future move, release and double-click events. + """ item: GraphicsComponentItem = self.scene().mouseGrabberItem() self._signals.component_selected.emit(item.op_id) # self.component_selected.emit(item.op_id) @@ -267,7 +268,8 @@ class GraphicsGraphEvent: # PyQt5 ) -> None: """Stores the current position in item's parent coordinates. 'event' will by default be accepted, and this item is then the mouse grabber. This - allows the item to receive future move, release and double-click events.""" + allows the item to receive future move, release and double-click events. + """ item: GraphicsTimelineItem = self.scene().mouseGrabberItem() self._delta_time = 0 item.set_text(self._delta_time) diff --git a/b_asic/scheduler_gui/graphics_graph_item.py b/b_asic/scheduler_gui/graphics_graph_item.py index f0a46623f9822ef2d98970c83fe4fa2c704a6a6a..8a3c43dd6d85c9e84caca97007e37bff26a92a17 100644 --- a/b_asic/scheduler_gui/graphics_graph_item.py +++ b/b_asic/scheduler_gui/graphics_graph_item.py @@ -41,7 +41,8 @@ class GraphicsGraphItem( def __init__( self, schedule: Schedule, parent: Optional[QGraphicsItem] = None ): - """Constructs a GraphicsGraphItem. 'parent' is passed to QGraphicsItemGroup's constructor.""" + """Constructs a GraphicsGraphItem. 'parent' is passed to QGraphicsItemGroup's constructor. + """ # QGraphicsItemGroup.__init__(self, self) # GraphicsGraphEvent.__init__(self) super().__init__(parent=parent) @@ -59,7 +60,8 @@ class GraphicsGraphItem( self._make_graph() def clear(self) -> None: - """Sets all children's parent to 'None' and delete the children objects.""" + """Sets all children's parent to 'None' and delete the children objects. + """ self._event_items = [] for item in self.childItems(): item.setParentItem(None) diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index 42b2cc58eede45ef9fdb3082436a449d6a721490..6d834b0b308c921143339ac4bec6f1dfbb629988 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -47,6 +47,7 @@ from b_asic.schedule import Schedule from b_asic.scheduler_gui.graphics_axes_item import GraphicsAxesItem from b_asic.scheduler_gui.graphics_component_item import GraphicsComponentItem from b_asic.scheduler_gui.graphics_graph_item import GraphicsGraphItem + sys.path.insert(0, "icons/") # Needed for *.rc.py files in ui_main_window from b_asic.scheduler_gui.ui_main_window import Ui_MainWindow @@ -424,7 +425,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.update_statusbar(self.tr("Schedule loaded successfully")) def update_statusbar(self, msg: str) -> None: - """Takes in an str and write 'msg' to the statusbar with temporarily policy.""" + """Takes in an str and write 'msg' to the statusbar with temporarily policy. + """ self.statusbar.showMessage(msg) def _write_settings(self) -> None: diff --git a/b_asic/scheduler_gui/ui_main_window.py b/b_asic/scheduler_gui/ui_main_window.py index b84799fa7357bdbec36a6ab87d754ac6701dc2ac..f197afcdaf82b2c4cd17bfbe4d09fbfdc68da4f8 100644 --- a/b_asic/scheduler_gui/ui_main_window.py +++ b/b_asic/scheduler_gui/ui_main_window.py @@ -15,19 +15,31 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(800, 600) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + MainWindow.sizePolicy().hasHeightForWidth() + ) MainWindow.setSizePolicy(sizePolicy) icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(":/icons/basic/small_logo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon.addPixmap( + QtGui.QPixmap(":/icons/basic/small_logo.png"), + QtGui.QIcon.Normal, + QtGui.QIcon.Off, + ) MainWindow.setWindowIcon(icon) self.centralwidget = QtWidgets.QWidget(MainWindow) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) + sizePolicy.setHeightForWidth( + self.centralwidget.sizePolicy().hasHeightForWidth() + ) self.centralwidget.setSizePolicy(sizePolicy) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) @@ -39,15 +51,28 @@ class Ui_MainWindow(object): self.splitter.setHandleWidth(0) self.splitter.setObjectName("splitter") self.view = QtWidgets.QGraphicsView(self.splitter) - self.view.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.view.setRenderHints(QtGui.QPainter.HighQualityAntialiasing|QtGui.QPainter.TextAntialiasing) - self.view.setViewportUpdateMode(QtWidgets.QGraphicsView.FullViewportUpdate) + self.view.setAlignment( + QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop + ) + self.view.setRenderHints( + QtGui.QPainter.HighQualityAntialiasing + | QtGui.QPainter.TextAntialiasing + ) + self.view.setViewportUpdateMode( + QtWidgets.QGraphicsView.FullViewportUpdate + ) self.view.setObjectName("view") self.info_table = QtWidgets.QTableWidget(self.splitter) - self.info_table.setStyleSheet("alternate-background-color: #fadefb;background-color: #ebebeb;") - self.info_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.info_table.setStyleSheet( + "alternate-background-color: #fadefb;background-color: #ebebeb;" + ) + self.info_table.setEditTriggers( + QtWidgets.QAbstractItemView.NoEditTriggers + ) self.info_table.setAlternatingRowColors(True) - self.info_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.info_table.setSelectionBehavior( + QtWidgets.QAbstractItemView.SelectRows + ) self.info_table.setRowCount(2) self.info_table.setColumnCount(2) self.info_table.setObjectName("info_table") @@ -56,10 +81,10 @@ class Ui_MainWindow(object): item = QtWidgets.QTableWidgetItem() self.info_table.setVerticalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() - item.setTextAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignVCenter) + item.setTextAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignVCenter) self.info_table.setHorizontalHeaderItem(0, item) item = QtWidgets.QTableWidgetItem() - item.setTextAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignVCenter) + item.setTextAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignVCenter) self.info_table.setHorizontalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() font = QtGui.QFont() @@ -72,7 +97,13 @@ class Ui_MainWindow(object): brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) brush.setStyle(QtCore.Qt.SolidPattern) item.setForeground(brush) - item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEditable|QtCore.Qt.ItemIsDragEnabled|QtCore.Qt.ItemIsDropEnabled|QtCore.Qt.ItemIsUserCheckable) + item.setFlags( + QtCore.Qt.ItemIsSelectable + | QtCore.Qt.ItemIsEditable + | QtCore.Qt.ItemIsDragEnabled + | QtCore.Qt.ItemIsDropEnabled + | QtCore.Qt.ItemIsUserCheckable + ) self.info_table.setItem(0, 0, item) item = QtWidgets.QTableWidgetItem() font = QtGui.QFont() @@ -85,7 +116,13 @@ class Ui_MainWindow(object): brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) brush.setStyle(QtCore.Qt.SolidPattern) item.setForeground(brush) - item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEditable|QtCore.Qt.ItemIsDragEnabled|QtCore.Qt.ItemIsDropEnabled|QtCore.Qt.ItemIsUserCheckable) + item.setFlags( + QtCore.Qt.ItemIsSelectable + | QtCore.Qt.ItemIsEditable + | QtCore.Qt.ItemIsDragEnabled + | QtCore.Qt.ItemIsDropEnabled + | QtCore.Qt.ItemIsUserCheckable + ) self.info_table.setItem(1, 0, item) self.info_table.horizontalHeader().setHighlightSections(False) self.info_table.horizontalHeader().setStretchLastSection(True) @@ -125,8 +162,16 @@ class Ui_MainWindow(object): self.menu_node_info.setCheckable(True) self.menu_node_info.setChecked(True) icon1 = QtGui.QIcon() - icon1.addPixmap(QtGui.QPixmap(":/icons/misc/right_panel.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) - icon1.addPixmap(QtGui.QPixmap(":/icons/misc/right_filled_panel.svg"), QtGui.QIcon.Normal, QtGui.QIcon.On) + icon1.addPixmap( + QtGui.QPixmap(":/icons/misc/right_panel.svg"), + QtGui.QIcon.Normal, + QtGui.QIcon.Off, + ) + icon1.addPixmap( + QtGui.QPixmap(":/icons/misc/right_filled_panel.svg"), + QtGui.QIcon.Normal, + QtGui.QIcon.On, + ) self.menu_node_info.setIcon(icon1) self.menu_node_info.setIconVisibleInMenu(False) self.menu_node_info.setObjectName("menu_node_info") @@ -195,19 +240,33 @@ class Ui_MainWindow(object): self.menu_Edit.setTitle(_translate("MainWindow", "&Edit")) self.menuWindow.setTitle(_translate("MainWindow", "&Window")) self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar")) - self.menu_load_from_file.setText(_translate("MainWindow", "&Load Schedule From File...")) - self.menu_load_from_file.setToolTip(_translate("MainWindow", "Load schedule from python script")) - self.menu_load_from_file.setShortcut(_translate("MainWindow", "Ctrl+O")) + self.menu_load_from_file.setText( + _translate("MainWindow", "&Load Schedule From File...") + ) + self.menu_load_from_file.setToolTip( + _translate("MainWindow", "Load schedule from python script") + ) + self.menu_load_from_file.setShortcut( + _translate("MainWindow", "Ctrl+O") + ) self.menu_save.setText(_translate("MainWindow", "&Save")) self.menu_save.setToolTip(_translate("MainWindow", "Save schedule")) self.menu_save.setShortcut(_translate("MainWindow", "Ctrl+S")) self.menu_node_info.setText(_translate("MainWindow", "&Node Info")) - self.menu_node_info.setToolTip(_translate("MainWindow", "Show node information")) + self.menu_node_info.setToolTip( + _translate("MainWindow", "Show node information") + ) self.menu_node_info.setShortcut(_translate("MainWindow", "Ctrl+I")) self.menu_quit.setText(_translate("MainWindow", "&Quit")) self.menu_quit.setShortcut(_translate("MainWindow", "Ctrl+Q")) self.menu_save_as.setText(_translate("MainWindow", "Save &As...")) - self.menu_exit_dialog.setText(_translate("MainWindow", "&Hide exit dialog")) - self.menu_exit_dialog.setToolTip(_translate("MainWindow", "Hide exit dialog")) + self.menu_exit_dialog.setText( + _translate("MainWindow", "&Hide exit dialog") + ) + self.menu_exit_dialog.setToolTip( + _translate("MainWindow", "Hide exit dialog") + ) self.actionT.setText(_translate("MainWindow", "T")) - self.menu_close_schedule.setText(_translate("MainWindow", "&Close Schedule")) + self.menu_close_schedule.setText( + _translate("MainWindow", "&Close Schedule") + ) diff --git a/b_asic/signal.py b/b_asic/signal.py index 594afa3cc8b07533d403cb5cecba06fb0c81c104..90af747f7b16bd685c317c868dad11357cf8b708 100644 --- a/b_asic/signal.py +++ b/b_asic/signal.py @@ -90,7 +90,8 @@ class Signal(AbstractGraphComponent): def remove_source(self) -> None: """Disconnect the source OutputPort of the signal. If the source port - still is connected to this signal then also disconnect the source port.""" + still is connected to this signal then also disconnect the source port. + """ src = self._source if src is not None: self._source = None diff --git a/b_asic/signal_flow_graph.py b/b_asic/signal_flow_graph.py index c200dc699d7287e457f9cd3337c0f65834310f28..b724437b91d114bf6b3880a73882c15469730cd2 100644 --- a/b_asic/signal_flow_graph.py +++ b/b_asic/signal_flow_graph.py @@ -297,7 +297,8 @@ class SFG(AbstractOperation): def __call__( self, *src: Optional[SignalSourceProvider], name: Name = "" ) -> "SFG": - """Get a new independent SFG instance that is identical to this SFG except without any of its external connections.""" + """Get a new independent SFG instance that is identical to this SFG except without any of its external connections. + """ return SFG( inputs=self._input_operations, outputs=self._output_operations, @@ -379,7 +380,8 @@ class SFG(AbstractOperation): def connect_external_signals_to_components(self) -> bool: """Connects any external signals to this SFG's internal operations. This SFG becomes unconnected to the SFG - it is a component off, causing it to become invalid afterwards. Returns True if succesful, False otherwise.""" + it is a component off, causing it to become invalid afterwards. Returns True if succesful, False otherwise. + """ if len(self.inputs) != len(self.input_operations): raise IndexError( f"Number of inputs does not match the number of" @@ -411,12 +413,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.""" + """Get the 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.""" + """Get the internal output operations in the same order as their respective output ports. + """ return self._output_operations def split(self) -> Iterable[Operation]: @@ -471,7 +475,8 @@ class SFG(AbstractOperation): @property def id_number_offset(self) -> GraphIDNumber: - """Get the graph id number offset of the graph id generator for this SFG.""" + """Get the graph id number offset of the graph id generator for this SFG. + """ return self._graph_id_generator.id_number_offset @property @@ -612,7 +617,8 @@ class SFG(AbstractOperation): def remove_operation(self, operation_id: GraphID) -> "SFG": """Returns a version of the SFG where the operation with the specified GraphID removed. The operation has to have the same amount of input- and output ports or a ValueError will - be raised. If no operation with the entered operation_id is found then returns None and does nothing.""" + be raised. If no operation with the entered operation_id is found then returns None and does nothing. + """ sfg_copy = self() operation = sfg_copy.find_by_id(operation_id) if operation is None: @@ -756,7 +762,8 @@ class SFG(AbstractOperation): def get_operations_topological_order(self) -> Iterable[Operation]: """Returns an Iterable of the Operations in the SFG in Topological Order. Feedback loops makes an absolutely correct Topological order impossible, so an - approximative Topological Order is returned in such cases in this implementation.""" + approximative Topological Order is returned in such cases in this implementation. + """ if self._operations_topological_order: return self._operations_topological_order @@ -791,7 +798,6 @@ class SFG(AbstractOperation): while operations_left > 0: while not p_queue.empty(): - op = p_queue.get()[2] operations_left -= 1 @@ -869,14 +875,16 @@ class SFG(AbstractOperation): def set_execution_time_of_type( self, type_name: TypeName, execution_time: int ) -> None: - """Set the execution time of all components with the given type name.""" + """Set the execution time of all components with the given type name. + """ for op in self.find_by_type_name(type_name): op.execution_time = execution_time def set_latency_offsets_of_type( self, type_name: TypeName, latency_offsets: Dict[str, int] ) -> None: - """Set the latency offset of all components with the given type name.""" + """Set the latency offset of all components with the given type name. + """ for op in self.find_by_type_name(type_name): op.set_latency_offsets(latency_offsets) diff --git a/b_asic/simulation.py b/b_asic/simulation.py index c4b35937ef4a87eca0d5b387bfc8e529ec128053..99d470c74aafee4b41990f6aba06db8e218cf51d 100644 --- a/b_asic/simulation.py +++ b/b_asic/simulation.py @@ -68,7 +68,8 @@ class Simulation: self.set_inputs(input_providers) def set_input(self, index: int, input_provider: InputProvider) -> None: - """Set the input function used to get values for the specific input at the given index to the internal SFG.""" + """Set the input function used to get values for the specific input at the given index to the internal SFG. + """ if index < 0 or index >= len(self._input_functions): raise IndexError( "Input index out of range (expected" @@ -91,7 +92,8 @@ class Simulation: def set_inputs( self, input_providers: Sequence[Optional[InputProvider]] ) -> None: - """Set the input functions used to get values for the inputs to the internal SFG.""" + """Set the input functions used to get values for the inputs to the internal SFG. + """ if len(input_providers) != self._sfg.input_count: raise ValueError( "Wrong number of inputs supplied to simulation (expected" @@ -107,7 +109,8 @@ class Simulation: bits_override: Optional[int] = None, truncate: bool = True, ) -> Sequence[Number]: - """Run one iteration of the simulation and return the resulting output values.""" + """Run one iteration of the simulation and return the resulting output values. + """ return self.run_for(1, save_results, bits_override, truncate) def run_until( @@ -148,7 +151,8 @@ class Simulation: bits_override: Optional[int] = None, truncate: bool = True, ) -> Sequence[Number]: - """Run a given number of iterations of the simulation and return the output values of the last iteration.""" + """Run a given number of iterations of the simulation and return the output values of the last iteration. + """ return self.run_until( self._iteration + iterations, save_results, bits_override, truncate ) @@ -159,7 +163,8 @@ class Simulation: bits_override: Optional[int] = None, truncate: bool = True, ) -> Sequence[Number]: - """Run the simulation until the end of its input arrays and return the output values of the last iteration.""" + """Run the simulation until the end of its input arrays and return the output values of the last iteration. + """ if self._input_length is None: raise IndexError("Tried to run unlimited simulation") return self.run_until( @@ -185,5 +190,6 @@ class Simulation: self._results.clear() def clear_state(self) -> None: - """Clear all current state of the simulation, except for the results and iteration.""" + """Clear all current state of the simulation, except for the results and iteration. + """ self._delays.clear() diff --git a/docs_sphinx/api/process.rst b/docs_sphinx/api/process.rst new file mode 100644 index 0000000000000000000000000000000000000000..97a1bc656c1b54c8af28bfe6aaf2e7f42952aa45 --- /dev/null +++ b/docs_sphinx/api/process.rst @@ -0,0 +1,12 @@ +****************** +``b_asic.process`` +****************** + +.. inheritance-diagram:: b_asic.process + :parts: 1 + :top-classes: b_asic.process.Process + +.. automodule:: b_asic.process + :members: + :undoc-members: + :show-inheritance: diff --git a/pyproject.toml b/pyproject.toml index b08dd9b07a86d9375e2438aae056529a0c00ac50..6fba3216cc1bbfe82d117b399a0a58ae10eb0640 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,3 +51,8 @@ fallback_version = "0.0+UNKNOWN" [project.urls] homepage = "https://gitlab.liu.se/da/B-ASIC" documenation = "https://da.gitlab-pages.liu.se/B-ASIC/" + +[tool.black] +skip-string-normalization = true +preview = true +line-length = 79 diff --git a/test/test_process.py b/test/test_process.py new file mode 100644 index 0000000000000000000000000000000000000000..80dd2ab6bd77dc295747549fbce2d6dd348374eb --- /dev/null +++ b/test/test_process.py @@ -0,0 +1,11 @@ +import pytest + +from b_asic.process import PlainMemoryVariable + +def test_PlainMemoryVariable(): + mem = PlainMemoryVariable(3, 0, {4: 1, 5: 2}) + assert mem.write_port == 0 + assert mem.start_time == 3 + assert mem.execution_time == 2 + assert mem.life_times == (1, 2) + assert mem.read_ports == (4, 5)