diff --git a/b_asic/gui_utils/mpl_window.py b/b_asic/gui_utils/mpl_window.py index 7251ac38fd2e1a84e5f2f217211e39fac6703557..eec93e2c35cf1eecf16c2e197c2808ed1f6281ef 100644 --- a/b_asic/gui_utils/mpl_window.py +++ b/b_asic/gui_utils/mpl_window.py @@ -12,7 +12,7 @@ class MPLWindow(QDialog): Dialog for plotting Matplotlib things. """ - def __init__(self, title: str = "B-ASIC"): + def __init__(self, title: str = "B-ASIC", subplots=(1, 1)): super().__init__() self.setWindowFlags( Qt.WindowTitleHint @@ -26,7 +26,7 @@ class MPLWindow(QDialog): self.setLayout(self._dialog_layout) self._plot_fig = Figure(figsize=(5, 4), layout="compressed") - self._plot_axes = self._plot_fig.add_subplot(111) + self._plot_axes = self._plot_fig.subplots(*subplots) self._plot_canvas = FigureCanvas(self._plot_fig) self._toolbar = NavigationToolbar(self._plot_canvas, self) diff --git a/b_asic/resources.py b/b_asic/resources.py index fedad2d90c5a007cc9aa386ea6a7758baf9f7d94..08668aecdf7ed889eadbcb79c80f56d8601dee21 100644 --- a/b_asic/resources.py +++ b/b_asic/resources.py @@ -1458,6 +1458,43 @@ class ProcessCollection: return dict(sorted(Counter(accesses).items())) + def show_port_accesses(self, title: str = ""): + """ + Show read, write, and total accesses. + + Parameters + ---------- + title : str + Figure title. + """ + fig, axes = plt.subplots(3, 1) + self.plot_port_accesses(axes) + if title: + fig.suptitle(title) + fig.show() # type: ignore + + def plot_port_accesses(self, axes): + """ + Plot read, write, and total accesses. + + This is plot as bar graphs. + + Parameters + ---------- + axes : list of three :class:`matplotlib.axes.Axes` + Three Axes to plot in. + """ + axes[0].bar(*zip(*self.read_port_accesses().items())) + axes[0].set_title("Read port accesses") + axes[1].bar(*zip(*self.write_port_accesses().items())) + axes[1].set_title("Write port accesses") + axes[2].bar(*zip(*self.total_port_accesses().items())) + axes[2].set_title("Total port accesses") + for ax in axes: + ax.xaxis.set_major_locator(MaxNLocator(integer=True, min_n_ticks=1)) + ax.yaxis.set_major_locator(MaxNLocator(integer=True, min_n_ticks=1)) + ax.set_xlim(-0.5, self.schedule_time - 0.5) + def from_name(self, name: str): """ Get a :class:`~b_asic.process.Process` from its name. diff --git a/b_asic/scheduler_gui/axes_item.py b/b_asic/scheduler_gui/axes_item.py index 43d56c9dfb74a4c9399a8584a59f88c3834f3efd..65520ea6a81703da518800fdc1883a8b1ba3b2eb 100644 --- a/b_asic/scheduler_gui/axes_item.py +++ b/b_asic/scheduler_gui/axes_item.py @@ -30,8 +30,8 @@ class AxesItem(QGraphicsItemGroup): Parameters ---------- - width - height + width : int + height : float width_indent : float, default: {SCHEDULE_INDENT} height_indent : float, default: {SCHEDULE_INDENT} width_padding : float, default: 0.6 diff --git a/b_asic/scheduler_gui/compile.py b/b_asic/scheduler_gui/compile.py index 8095aa4c361cddf6bee7821102e5dcc79b9af3dd..2d9be5282811344e6a95548244e85a9195a1dfde 100644 --- a/b_asic/scheduler_gui/compile.py +++ b/b_asic/scheduler_gui/compile.py @@ -2,7 +2,7 @@ """ B-ASIC Scheduler-gui Resource and Form Compiler Module. -Compiles Qt5 resource and form files. Requires PySide2 or PyQt5 to be installed. +Compile Qt5 resource and form files. Requires PySide2 or PyQt5 to be installed. If no arguments is given, the compiler search for and compiles all form (.ui) files. """ @@ -37,8 +37,9 @@ def _check_filenames(*filenames: str) -> None: def _check_qt_version() -> None: """ - Check if PySide2, PyQt5, PySide6, or PyQt6 is installed, otherwise raise - AssertionError exception. + Check if PySide2, PyQt5, PySide6, or PyQt6 is installed. + + Otherwise, raise AssertionError exception. """ assert ( uic.PYSIDE2 or uic.PYQT5 or uic.PYSIDE6 or uic.PYQT6 @@ -46,8 +47,15 @@ def _check_qt_version() -> None: def replace_qt_bindings(filename: str) -> None: - """Replaces qt-binding API in *filename* from PySide2/6 or PyQt5/6 to qtpy.""" - with open(f"{filename}", "r") as file: + """ + Replace qt-binding API in *filename* from PySide2/6 or PyQt5/6 to qtpy. + + Parameters + ---------- + filename : str + The name of the file to replace bindings in. + """ + with open(f"{filename}") as file: filedata = file.read() filedata = filedata.replace("from PyQt5", "from qtpy") filedata = filedata.replace("from PySide2", "from qtpy") @@ -59,8 +67,15 @@ def replace_qt_bindings(filename: str) -> None: 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. + Compile resource file(s) given by *filenames*. + + If no arguments are given, the compiler will search for resource (.qrc) files and + compile accordingly. + + Parameters + ---------- + *filenames : str + One or more file names. """ _check_qt_version() @@ -127,8 +142,15 @@ def compile_rc(*filenames: str) -> None: def compile_ui(*filenames: str) -> None: """ - Compile form file(s) given by *filenames*. If no arguments are given, the - compiler will search for form (.ui) files and compile accordingly. + Compile form file(s) given by *filenames*. + + If no arguments are given, the compiler will search for form (.ui) files and + compile accordingly. + + Parameters + ---------- + *filenames : str + One or more file names. """ _check_qt_version() @@ -228,6 +250,8 @@ def compile_ui(*filenames: str) -> None: def compile_all() -> None: """ + Compile all .qrc and .ui files. + The compiler will search for resource (.qrc) files and form (.ui) files and compile accordingly. """ @@ -246,37 +270,26 @@ if __name__ == "__main__": "-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" - ), - ) - 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( + "--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( "--all", diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index c82466c92b5e0b34a7811bf8ff00fd9eb463dcfa..5f729f36587a9e2ac015b93d295fd9cecf63bf2c 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -164,6 +164,9 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): self.action_view_variables.triggered.connect( self._show_execution_times_for_variables ) + # self.action_view_port_accesses.triggered.connect( + # self._show_ports_accesses_for_storage + # ) self.actionZoom_to_fit.setIcon(get_icon('zoom-to-fit')) self.actionZoom_to_fit.triggered.connect(self._zoom_to_fit) self.actionToggle_full_screen.setIcon(get_icon('full-screen')) @@ -847,6 +850,16 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): ) self._execution_time_for_variables.show() + def _show_ports_accesses_for_storage(self): + self._ports_accesses_for_storage = MPLWindow( + "Port accesses for storage", subplots=(3, 1) + ) + mem_vars = self._schedule.get_memory_variables() + _, mem_vars = mem_vars.split_on_length() + + mem_vars.plot_port_accesses(self._ports_accesses_for_storage.axes) + self._ports_accesses_for_storage.show() + def _update_recent_file_list(self): settings = QSettings() diff --git a/b_asic/scheduler_gui/timeline_item.py b/b_asic/scheduler_gui/timeline_item.py index d2d82d399d7f50fdddcbb9023370632dd8af92c2..048a92a025645a9f34319d71ff0673245df0f092 100644 --- a/b_asic/scheduler_gui/timeline_item.py +++ b/b_asic/scheduler_gui/timeline_item.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ B-ASIC Scheduler-GUI Timeline Item Module. @@ -78,7 +77,14 @@ class TimelineItem(QGraphicsLineItem): # return self._delta_time_label def set_text(self, number: int) -> None: - """Set the label text to *number*.""" + """ + Set the label text to *number*. + + Parameters + ---------- + number : int + The label number. + """ # self.prepareGeometryChange() self._delta_time_label.setPlainText(f"( {number:+} )") self._delta_time_label.setX( @@ -106,6 +112,14 @@ class TimelineItem(QGraphicsLineItem): self._delta_time_label.hide() def set_text_scale(self, scale: float) -> None: + """ + Set the text scale. + + Parameters + ---------- + scale : float + The text scale. + """ self._delta_time_label.setScale(scale) @property diff --git a/b_asic/simulation.py b/b_asic/simulation.py index 2a6e7b6e81c2569aec7aa1a63ea891b05e060720..31f25910432b1be5ea7cee5aec2a98547bd1375b 100644 --- a/b_asic/simulation.py +++ b/b_asic/simulation.py @@ -106,6 +106,10 @@ 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. + + Parameters + ---------- + input_providers : array of list, callable, or number """ if len(input_providers) != self._sfg.input_count: raise ValueError( @@ -122,7 +126,14 @@ class Simulation: bits_override: Optional[int] = None, quantize: bool = True, ) -> Sequence[Num]: - """Run one iteration of the simulation and return the resulting output values. + """ + Run one iteration of the simulation and return the resulting output values. + + Parameters + ---------- + save_results : bool, default: True + bits_override : int, optional + quantize : bool, default: True """ return self.run_for(1, save_results, bits_override, quantize) @@ -134,10 +145,19 @@ class Simulation: quantize: bool = True, ) -> Sequence[Num]: """ - Run the simulation a given number of iterations. + Run the simulation until the iteration number. Will run until the number of iterations is greater than or equal to the given - iteration and return the output values of the last iteration. + iteration and return the output values of the last iteration. The number of + iterations actually simulated depends on the current state of the Simulation. + + Parameters + ---------- + iteration : int + Iteration number to stop the simulation at. + save_results : bool, default: True + bits_override : int, optional + quantize : bool, default: True """ result: Sequence[Num] = [] while self._iteration < iteration: @@ -171,6 +191,14 @@ class Simulation: Run a given number of iterations of the simulation. Return the output values of the last iteration. + + Parameters + ---------- + iterations : int + Number of iterations to simulate. + save_results : bool, default: True + bits_override : int, optional + quantize : bool, default: True """ return self.run_until( self._iteration + iterations, save_results, bits_override, quantize @@ -186,6 +214,12 @@ class Simulation: Run the simulation until the end of its input arrays. Return the output values of the last iteration. + + Parameters + ---------- + save_results : bool, default: True + bits_override : int, optional + quantize : bool, default: True """ if self._input_length is None: raise IndexError("Tried to run unlimited simulation") @@ -199,7 +233,7 @@ class Simulation: @property def results(self) -> ResultArrayMap: """ - Get a mapping from result keys to numpy arrays containing all results. + A mapping from result keys to numpy arrays containing all results. This includes intermediate values, calculated for each iteration up until now that was run with *save_results* enabled.