diff --git a/src/simudator/core/modules/memory.py b/src/simudator/core/modules/memory.py index 492601d6a4f3916924536123d90463391f4ac381..163d1f7f51a351dd247275dc18900502700eacbe 100644 --- a/src/simudator/core/modules/memory.py +++ b/src/simudator/core/modules/memory.py @@ -33,7 +33,7 @@ class Memory(Module): super().__init__(signals, name) # Internal state - self.memory = [0 for i in range(size)] + self.memory = [0 for _ in range(size)] self.current_address = 0 self.is_write = False diff --git a/src/simudator/gui/gui.py b/src/simudator/gui/gui.py index 88f8a27d084ea39cd1d6e3c769693f0ef165f56f..825164f619a955d230c9b401701ba3ab7891ebc8 100644 --- a/src/simudator/gui/gui.py +++ b/src/simudator/gui/gui.py @@ -226,14 +226,18 @@ class GUI(QMainWindow): self.breakpoint_action.triggered.connect(self.openBreakpointWindow) # Create 'update value' window button - self.update_value_action = QAction("Update values while running", self, checkable=True) + self.update_value_action = QAction( + "Update values while running", self, checkable=True + ) self.update_value_action.setChecked(False) self.update_value_action.setStatusTip("Toggle value updates while running.") self.update_value_action.triggered.connect(self.toggle_value_update_on_run) # Create 'set delay' window button self.set_delay_action = QAction("Set update delay", self) - self.set_delay_action.setStatusTip("Sets the delay between each update when the cpu is running.") + self.set_delay_action.setStatusTip( + "Sets the delay between each update when the cpu is running." + ) self.set_delay_action.triggered.connect(self.set_update_delay) # Create Tools menu for tool actions actions @@ -256,7 +260,7 @@ class GUI(QMainWindow): self.stop_action.triggered.connect(self.stopToolBarButtonClick) toolbar.addAction(self.stop_action) - # Add Asm label + # Add Asm label self.clock_cycle_label = QLabel("Clock cycle: ", self) toolbar.addWidget(self.clock_cycle_label) @@ -303,7 +307,7 @@ class GUI(QMainWindow): spacing.setFixedWidth(10) toolbar.addWidget(spacing) - # Add Asm label + # Add Asm label self.asm_label = QLabel("Assembler Instructions: ", self) toolbar.addWidget(self.asm_label) @@ -370,7 +374,7 @@ class GUI(QMainWindow): def updateCpuClockCycle(self) -> None: """ - Update the clock cycle counter. + Update the clock cycle counter. Used while the program is running to show the user nothing has crashed. """ @@ -392,7 +396,6 @@ class GUI(QMainWindow): if self.breakpoint_window is not None: self.breakpoint_window.update() - """ @Slot is used to explicitly mark a python method as a Qt slot and specify a C++ signature for it, which is used most commonly @@ -503,7 +506,9 @@ class GUI(QMainWindow): steps = self.jump_value_box.value() self.cpu_running = True self.setDisabledWhenRunning(True) - simulation_thread = RunThread(self.cpu, self.cpu_tick_signal, self.update_delay, False, False, steps) + simulation_thread = RunThread( + self.cpu, self.cpu_tick_signal, self.update_delay, False, False, steps + ) self.threadpool.start(simulation_thread) def stepAsmToolBarButtonClick(self): @@ -518,7 +523,9 @@ class GUI(QMainWindow): steps = self.asm_jump_value_box.value() self.cpu_running = True self.setDisabledWhenRunning(True) - simultaion_thread = RunThread(self.cpu, self.cpu_tick_signal, self.update_delay, False, True, steps) + simultaion_thread = RunThread( + self.cpu, self.cpu_tick_signal, self.update_delay, False, True, steps + ) self.threadpool.start(simultaion_thread) self.updateCpuListeners() @@ -550,8 +557,8 @@ class GUI(QMainWindow): if self.update_all_values: self.updateCpuListeners() - # A signal of 0 steps signifies end of execution, i.e. the CPU has - # halted or run the specified amount of ticks + # A signal of 0 steps signifies end of execution, i.e. the CPU has + # halted or run the specified amount of ticks # => Enable the relevant parts of the GUI again if steps == 0: self.cpu_running = False @@ -560,7 +567,9 @@ class GUI(QMainWindow): # Inform user of reached break point if self.cpu.breakpoint_reached: - self.messageBox("Reached breakpoint: " + self.cpu.last_breakpoint.__str__()) + self.messageBox( + "Reached breakpoint: " + self.cpu.last_breakpoint.__str__() + ) # Inform user of halt if self.cpu.should_halt(): @@ -836,13 +845,15 @@ class GUI(QMainWindow): """ Toggles whether all values or only clock cycle is being updated each tick. """ - self.update_all_values = not self.update_all_values + self.update_all_values = not self.update_all_values def set_update_delay(self): """ Sets the update delay for the visual updates while the cpu is running. """ - delay, ok = QInputDialog.getDouble(self, "Input Dialog", "Enter a float value:", decimals=5) + delay, ok = QInputDialog.getDouble( + self, "Input Dialog", "Enter a float value:", decimals=5 + ) if ok: self.update_delay = delay diff --git a/src/simudator/gui/module_graphics_item/memory_graphic.py b/src/simudator/gui/module_graphics_item/memory_graphic.py index a39999626666c8470049c8676190c6b588cd9652..2817042cc772ef63fbbce36395542b585fda8eec 100644 --- a/src/simudator/gui/module_graphics_item/memory_graphic.py +++ b/src/simudator/gui/module_graphics_item/memory_graphic.py @@ -1,3 +1,5 @@ +from math import ceil + from qtpy.QtCore import Qt from qtpy.QtCore import Signal as pyqtSignal from qtpy.QtCore import Slot @@ -5,9 +7,8 @@ from qtpy.QtWidgets import ( QAction, QGraphicsRectItem, QGraphicsSimpleTextItem, - QTextEdit, - QVBoxLayout, - QWidget, + QTableWidget, + QTableWidgetItem, ) from simudator.core.modules import Memory @@ -17,36 +18,84 @@ from simudator.gui.orientation import Orientation from simudator.gui.port_graphics_item import PortGraphicsItem -class MemoryWindow(QWidget): - """ - Widget for showing content of memory +class MemoryWindow(QTableWidget): """ + A class showing the contents of a memory module in a QTableWidget. + + This class assumes that the size of the memory module will remain constant. - _ROW_LENGTH = 5 + Parameters + ---------- + memory_module: An instance of the Memory base class. + column_size: An integer specifying the number of columns, optional. + + """ - def __init__(self, memory_module: Memory): + def __init__(self, memory_module: Memory, column_size=-1): super().__init__() - self.module = memory_module + self._memory = memory_module + self._column_size = column_size + self._memory_size = len(self._memory.get_state()["memory"]) + self._set_column_size() + self.setColumnCount(self._column_size) + self.setRowCount(ceil(self._memory_size / self._column_size)) + self.setHorizontalHeaderLabels(["+" + str(i) for i in range(4)]) - self.text = QTextEdit("") - layout = QVBoxLayout() - layout.addWidget(self.text) - self.setLayout(layout) + vertical_headers = [] + for i in range(0, self._memory_size, self._column_size): + vertical_headers.append(str(hex(i))) + + self.setVerticalHeaderLabels(vertical_headers) self.update() def update(self): - memory_str = "" - for address, value in enumerate(self.module.memory): - # Add address and content to string - # Make sure its unifrom lenght so rows are consistent - memory_str += f"{address}" + ": " + f"{value}" + " " + "\t" + """ + Update the content of this widget to reflect the content of the memory module. + """ + memory_content = self._memory.get_state()["memory"] + for i in range(self._memory_size): + value = memory_content[i] + row = i // self._column_size + col = i % self._column_size + self.set_item(row, col, str(value)) - # Make new line when we reach end of row - if address % self._ROW_LENGTH == self._ROW_LENGTH - 1: - memory_str += "\n" + def set_item(self, row: int, col: int, text: str) -> None: + """Set the text at specified table cell to the given text. + + Parameters + ---------- + row: int + The items row position in the pipeline diagram. + col: int + The items column position in the pipeline diagram. + text: str + The text to be displayed. + """ + item = QTableWidgetItem(text) - self.text.setText(memory_str) + self.setItem(row, col, item) + + def _set_column_size(self) -> None: + """ + Set the column size to a reasonable value if the size was not given to the constructor. + + This function assumes that the attributes `column_size` and `memory_size` are set before it is called. + """ + + if not self._column_size == -1: + return + + if self._memory_size > 200: + self._column_size = 4 + return + + if self._memory_size > 100: + self._column_size = 2 + return + + self._column_size = 1 + return class MemoryGraphicsItem(ModuleGraphicsItem): diff --git a/src/simudator/gui/pipeline.py b/src/simudator/gui/pipeline.py index 2c400c06f93411d368b117796136d577687c94f0..9023ef17475fa40212b42481e56e0235b0a12bf7 100644 --- a/src/simudator/gui/pipeline.py +++ b/src/simudator/gui/pipeline.py @@ -1,6 +1,8 @@ -from qtpy.QtWidgets import QTableWidget, QTableWidgetItem from typing import Any +from qtpy.QtWidgets import QTableWidget, QTableWidgetItem + + # TODO: Make the table unediatable class PipeLine(QTableWidget): """ @@ -23,7 +25,6 @@ class PipeLine(QTableWidget): self.set_item(3, 3, self.instructions[3]) """ - def set_instruction(self, instructions: list[str]) -> None: """Give the pipeline the current CPU instructions.""" self.instructions = instructions @@ -43,7 +44,7 @@ class PipeLine(QTableWidget): Depending on the CPU architecture the labels for each row can change format. In a pipelined CPU we want to assign a clock cycle to each instruction. In an ARM cpu we want the current - CPU clock cycle for the first instruciton, and something else for the + CPU clock cycle for the first instruciton, and something else for the micro instrucitons. """ raise NotImplemented @@ -63,6 +64,3 @@ class PipeLine(QTableWidget): item = QTableWidgetItem(text) self.setItem(row, col, item) - - - diff --git a/src/simudator/processor/mia/gui/mia_memory_graphic.py b/src/simudator/processor/mia/gui/mia_memory_graphic.py index 07c7b46ed5d4db658e161506978dc4ec50de2c02..303ddb8d9cccd9b2e53139c4b9de9fe0fdb26766 100644 --- a/src/simudator/processor/mia/gui/mia_memory_graphic.py +++ b/src/simudator/processor/mia/gui/mia_memory_graphic.py @@ -11,44 +11,15 @@ from qtpy.QtWidgets import ( from simudator.core.modules import Memory from simudator.gui.color_scheme import ColorScheme as CS -from simudator.gui.module_graphics_item.memory_graphic import MemoryGraphicsItem +from simudator.gui.module_graphics_item.memory_graphic import ( + MemoryGraphicsItem, + MemoryWindow, +) from simudator.gui.orientation import Orientation from simudator.gui.port_graphics_item import PortGraphicsItem from simudator.processor.mia.gui.mia_memory_content_dialog import MiaMemoryContentDialog -class MiaMemoryWindow(QWidget): - """ - Widget for showing content of memory - """ - - ROW_LENGTH = 4 - - def __init__(self, memory_module: Memory): - super().__init__() - self.module = memory_module - - self.text = QTextEdit("") - layout = QVBoxLayout() - layout.addWidget(self.text) - self.setLayout(layout) - - self.update() - - def update(self): - memory_str = "" - for address, value in enumerate(self.module.memory): - # Add address and content to string - # Make sure its unifrom lenght so rows are consistent - memory_str += f"0x{address:02x}" + ": " + f"0x{value:04x}" + " " + "\t" - - # Make new line when we reach end of row - if address % self.ROW_LENGTH == self.ROW_LENGTH - 1: - memory_str += "\n" - - self.text.setText(memory_str) - - class MiaMemoryGraphicsItem(MemoryGraphicsItem): """ Graphics module for a Memory module. @@ -99,7 +70,7 @@ class MiaMemoryGraphicsItem(MemoryGraphicsItem): Create and show a MemoryWindow that displays the contents of the memory module associated with this graphics item. """ - self.memory_window = MiaMemoryWindow(self.module) + self.memory_window = MemoryWindow(self.module) self.memory_window.show() def memoryBreakpointDialog(self) -> None: