diff --git a/src/simudator/core/modules/__init__.py b/src/simudator/core/modules/__init__.py index 8fca4073c3390336e99c358c803e1cf73544be3a..1014c61a6c149020bac470771bf2c4f1730237b2 100644 --- a/src/simudator/core/modules/__init__.py +++ b/src/simudator/core/modules/__init__.py @@ -1,6 +1,7 @@ from .demux import Demux from .memory import Memory from .mux import Mux -from .register import Flag, IntegerRegister, Register +from .register import Register +from .integer_register import IntegerRegister -__all__ = ["Demux", "Flag", "IntegerRegister", "Memory", "Mux", "Register"] +__all__ = ["Demux", "IntegerRegister", "Memory", "Mux", "Register"] diff --git a/src/simudator/core/modules/integer_register.py b/src/simudator/core/modules/integer_register.py new file mode 100644 index 0000000000000000000000000000000000000000..cf338c237b5fb180d81a53805044dec2808b9a4f --- /dev/null +++ b/src/simudator/core/modules/integer_register.py @@ -0,0 +1,65 @@ +from typing import Any + +from simudator.core.modules.register import Register +from simudator.core.signal import Signal + + +class IntegerRegister(Register): + """ + A register intended to store integers only. + + Parameters + ---------- + input : Signal + Signal from which the value is stored in the register. + output : Signal + Signal onto which the value of the register is outputted. + bit_length : int + Maximum number of bits of the input to store in the register. All extra + bits of the input value are discarded. + value : Any + Initial value of the register. + name : str + Name of the register. + """ + + __slots__ = "_bit_length" + + def __init__( + self, + input: Signal, + output: Signal, + bit_length: int, + value: int = 0, + name: str | None = None, + ) -> None: + + # set the name + if name is None: + name = f"{bit_length}-bit register" + + super().__init__(input, output, value=value, name=name) + + # set the bit length of the register + self._bit_length = bit_length + + def update_register(self) -> None: + super().update_register() + mask = 2**self._bit_length - 1 + self._value = self._value & mask + + def save_state_to_file(self, file_path: str) -> bool: + content = self.name + ":\nvalue: " + hex(self._value)[2:] + "\n\n" + return super()._helper_save_state_to_file(file_path, content) + + def load_from_str(self, state_string) -> None: + string_pair = state_string.split(": ") + self._value = int(string_pair[1], 16) + + def get_parameter(self) -> dict[str, Any]: + """ + Return a dict of the register parameter, i.e. its bit length. + """ + parameter = super().get_parameter() + parameter["bit_length"] = self._bit_length + return parameter diff --git a/src/simudator/core/modules/register.py b/src/simudator/core/modules/register.py index 748c0c25e1a6909fc90f7ffef06b91d34b93ff79..3f43423edbf8467db642f22a96c72774f6bbb1c2 100644 --- a/src/simudator/core/modules/register.py +++ b/src/simudator/core/modules/register.py @@ -105,134 +105,3 @@ class Register(Module): "\n value: ", self._value, ) - - -class IntegerRegister(Register): - """ - A register intended to store integers only. - - Parameters - ---------- - input : Signal - Signal from which the value is stored in the register. - output : Signal - Signal onto which the value of the register is outputted. - bit_length : int - Maximum number of bits of the input to store in the register. All extra - bits of the input value are discarded. - value : Any - Initial value of the register. - name : str - Name of the register. - """ - - __slots__ = "_bit_length" - - def __init__( - self, - input: Signal, - output: Signal, - bit_length: int, - value: int = 0, - name: str | None = None, - ) -> None: - - # set the register name - if name is None: - name = f"{bit_length}-bit register" - - super().__init__(input, output, value=value, name=name) - - # set the bit length of the register - self._bit_length = bit_length - - def update_register(self) -> None: - super().update_register() - mask = 2**self._bit_length - 1 - self._value = self._value & mask - - def get_state(self) -> dict[str, Any]: - state = super().get_state() - return state - - def get_parameter(self) -> dict[str, Any]: - """ - Return a dict of the register parameter, i.e. its bit length. - """ - parameter = super().get_parameter() - parameter["bit_length"] = self._bit_length - return parameter - - def set_state(self, state: dict[str, Any]) -> None: - super().set_state(state) - - def save_state_to_file(self, file_path: str) -> None: - file = open(file_path, "a") - file.write(self.name + ":\n") - file.write("value: " + hex(self._value)[2:] + "\n\n") - file.close() - - def load_from_str(self, state_string): - string_pair = state_string.split(": ") - self._value = int(string_pair[1], 16) - - -class Flag(IntegerRegister): - """ - A simple module that can store a value. This module is also - updated during the 'update logic' phase. - """ - - def __init__( - self, - input: Signal, - output: Signal, - bit_length=1, - value=0, - name="Flag", - ) -> None: - - # set the flags name - super().__init__( - input=input, - output=output, - bit_length=bit_length, - value=value, - name=name, - ) - - def update_logic(self): - """ - The flag has no logic but it still need to be updated during - the update logic phase. This is due the fact that the flag - should update the same tick as the change that causes the flag - to activate (ex: loop counter reaches zero -> set L flag to 1) - """ - input_value = self.signals["in_content"].get_value() - mask = 2**self._bit_length - 1 - self._value = input_value & mask - - def get_gui_state(self) -> dict: - state = super().get_gui_state() - state["value"] = self._value - return state - - def save_state_to_file(self, file_path: str) -> None: - """ - Tries to save the modules state to a given file. - """ - file = open(file_path, "a") - file.write(self.name + ":\n") - file.write("value: " + str(self._value) + "\n\n") - file.close() - - def print_module(self) -> None: - print( - "", - self.name, - "\n -----", - "\n value: ", - self._value, - "\n bit length: ", - self._bit_length, - ) diff --git a/src/simudator/gui/gui.py b/src/simudator/gui/gui.py index 2f2aa64626b65082ff9a321e61de017495bfa6fc..9b3fdc9cb05e293a70869cc1c078057c23668475 100644 --- a/src/simudator/gui/gui.py +++ b/src/simudator/gui/gui.py @@ -3,7 +3,7 @@ import sys from qtpy import QtCore, QtWidgets from qtpy.QtCore import Signal as pyqtSignal -from qtpy.QtCore import Slot, ws +from qtpy.QtCore import Slot from qtpy.QtWidgets import ( QAction, QApplication, @@ -13,6 +13,7 @@ from qtpy.QtWidgets import ( ) from simudator.core.processor import Processor +from simudator.core.signal import Signal from simudator.gui.breakpoint_window import BreakpointWindow from simudator.gui.cpu_graphics_scene import CpuGraphicsScene from simudator.gui.dialogs.lambda_breakpoint_dialog import LambdaBreakpointDialog @@ -20,6 +21,7 @@ from simudator.gui.menu_bar import MainMenuBar from simudator.gui.module_graphics_item.module_graphics_item import ModuleGraphicsItem from simudator.gui.pipeline import PipeLine from simudator.gui.processor_handler import ProcessorHandler +from simudator.gui.signal_viewer import SignalViewer from simudator.gui.simulation_toolbar import SimulationToolBar from simudator.gui.view import View @@ -313,6 +315,21 @@ class GUI(QMainWindow): """ self._graphics_scene.load_layout_from_file(file_path) + def add_signal_viewer(self, signal: Signal, label: str | None = None) -> None: + """ + Add a signal viewer to the graphics scene. + + Parameters + ---------- + signal : Signal + Processor signal to view. + label : str | None + Optional label of the signal viewer. + """ + viewer = SignalViewer(signal, label) + self._processor_handler.changed.connect(viewer.update) + self._graphics_scene.addItem(viewer) + if __name__ == '__main__': QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) diff --git a/src/simudator/gui/processor_handler.py b/src/simudator/gui/processor_handler.py index 91612fd9a6e5af44e70f145e0745729c632d44a7..9a9e9b3321f5fcb82c00be6bd77dbd362751ab53 100644 --- a/src/simudator/gui/processor_handler.py +++ b/src/simudator/gui/processor_handler.py @@ -310,7 +310,7 @@ class ProcessorHandler(QObject): self._processor.undo_asm_instruction(instructions) self._signal_processor_changed() except (ValueError, IndexError): - self._error_msg_box("Unable to undo the instruction.") + self._error_msg_box.showMessage("Unable to undo the instruction.") @Slot() def toggle_value_update_on_run(self): diff --git a/src/simudator/gui/signal_viewer.py b/src/simudator/gui/signal_viewer.py new file mode 100644 index 0000000000000000000000000000000000000000..34aeb20e847fa34689781a710d0ccb6a242fabd1 --- /dev/null +++ b/src/simudator/gui/signal_viewer.py @@ -0,0 +1,206 @@ +from qtpy.QtCore import Property, QPointF, QRectF, Qt, Slot +from qtpy.QtGui import QBrush, QColor, QFont, QPainter, QPainterPath, QPen +from qtpy.QtWidgets import ( + QGraphicsItem, + QGraphicsWidget, + QStyleOptionGraphicsItem, + QWidget, +) + +from simudator.core.signal import Signal + + +class SignalViewer(QGraphicsWidget): + DEFAULT_WIDTH = 50 + DEFAULT_HEIGHT = 50 + + def __init__( + self, + signal: Signal, + label: str | None = None, + parent: QGraphicsItem | None = None, + flags: Qt.WindowFlags | Qt.WindowType = Qt.WindowFlags(), + ) -> None: + super().__init__(parent, flags) + self._signal = signal + self._label = label + self.setFlag(QGraphicsItem.ItemIsMovable) + self.resize(self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT) + + # Default values for properties for appearance + # TODO: Put these values in constants? + self._outline_width = 1 + self._outline = self.palette().windowText() + self._background = self.palette().window() + self._text_color = self.palette().windowText().color() + self._text_font = QFont("Sans Serif", 9) + + def paint( + self, + painter: QPainter | None = None, + option: QStyleOptionGraphicsItem | None = None, + widget: QWidget | None = None, + ) -> None: + painter.save() + + width = self.size().width() + height = self.size().height() + + # Draw the base shape of the signal viewer + pen = QPen(self.outline, self.outline_width) + painter.setPen(pen) + painter.setBrush(self.background) + painter.drawPath(self.shape()) + + # Set text specific painter settings + painter.setPen(QPen(self.text_color)) + painter.setBrush(QBrush(self.text_color)) + painter.setFont(self.text_font) + + # Draw label if any + value_y_start = 0 + if self._label is not None: + text_rect = QRectF(0, 0, width, height / 2) + painter.drawText( + text_rect, + Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap, + self._label, + ) + + # Make the signal value text be placed in the lower half of + # the signal viewer rather than in the middle + value_y_start = height / 2 + + # Draw signal value + text_rect = QRectF(0, value_y_start, width, height - value_y_start) + painter.drawText( + text_rect, + Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap, + str(self._signal.get_value()), + ) + + painter.restore() + + @Slot() + def update(self, rect: QRectF | None = None): + # This "override" is needed in order to decorate update() as a + # pyqt slot + super().update() + + def shape(self) -> QPainterPath: + path = QPainterPath() + path.addRect(0, 0, self.size().width(), self.size().height()) + return path + + def boundingRect(self) -> QRectF: + width = self.size().width() + height = self.size().height() + margin = self.outline_width / 2 + return QRectF(0 - margin, 0 - margin, width + margin, height + margin) + + def outline_width(self) -> float: + """Return the outline pen width. + + Returns + ------- + float + Outline pen width. + """ + return self._outline_width + + def set_outline_width(self, width: float) -> None: + """Set the outline pen width. + + Parameters + ---------- + width : float + Outline pen width. + + """ + self._outline_width = width + + def outline(self) -> QBrush: + """Return the outline brush used to create the outline pen. + + Returns + ------- + QBrush + Outline brush. + """ + return self._outline + + def set_outline(self, brush: QBrush) -> None: + """Set the outline brush used to create the outline pen. + + Parameters + ---------- + brush : QBrush + Outline brush. + """ + self._outline = brush + + def background(self) -> QBrush: + """Return the bursh used for filling the background. + + Returns + ------- + QBrush + Background brush. + """ + return self._background + + def set_background(self, brush: QBrush) -> None: + """Set the bursh used for filling the background. + + Parameters + ---------- + brush : QBrush + Background brush. + """ + self._background = brush + + def text_color(self) -> QColor: + """Return the color used for text. + + Returns + ------- + QColor + Text color. + """ + return self._text_color + + def set_text_color(self, color: QColor) -> None: + """Set the color used for text. + + Parameters + ---------- + color : QColor + Text color. + """ + self._text_color = color + + def text_font(self) -> QFont: + """Return the font used for text. + + Returns + ------- + QFont + Text font. + """ + return self._text_font + + def set_text_font(self, font: QFont) -> None: + """Set the font used for text. + + Parameters + ---------- + font : QFont + Text font. + """ + self._text_font = font + + outline_width = Property(float, outline_width, set_outline_width) + outline = Property(QBrush, outline, set_outline) + background = Property(QBrush, background, set_background) + text_color = Property(QColor, text_color, set_text_color) + text_font = Property(QFont, text_font, set_text_font) diff --git a/src/simudator/processor/mia/gui/mia_flag_graphic.py b/src/simudator/processor/mia/gui/mia_flag_graphic.py index 33f6c022d8b70fe6f71c7cd114fd9e4eb0cb151d..217ee15634da3f35910a7878ab41d9a361b2c42f 100644 --- a/src/simudator/processor/mia/gui/mia_flag_graphic.py +++ b/src/simudator/processor/mia/gui/mia_flag_graphic.py @@ -1,4 +1,4 @@ -from simudator.core.modules.register import IntegerRegister +from simudator.core.modules.integer_register import IntegerRegister from simudator.gui.module_graphics_item.register_graphic import ( IntegerRegisterGraphicsItem, ) diff --git a/src/simudator/processor/mia/mia.py b/src/simudator/processor/mia/mia.py index 5fbd52a3ec7c0ba8339f2d338868427a9f420956..eb52e58dabea5bc3e02b62ac1e12a4511046c7d5 100644 --- a/src/simudator/processor/mia/mia.py +++ b/src/simudator/processor/mia/mia.py @@ -1,9 +1,7 @@ import sys -from typing import Any from simudator.cli.cli import CLI -from simudator.core.modules import Flag -from simudator.core.modules.register import IntegerRegister +from simudator.core.modules.integer_register import IntegerRegister from simudator.core.processor import Processor, Signal from simudator.gui.gui import GUI from simudator.processor.mia.gui import ( @@ -236,11 +234,11 @@ class MIA_CPU(Processor): lc = LC(lc_uM, lc_bus, l_lc, lc_uM_uADR) # Flags - z_flag = Flag(z_alu, uM_z, name="Z-Flag") - n_flag = Flag(n_alu, uM_n, name="N-Flag") - c_flag = Flag(c_alu, uM_c, name="C-Flag") - o_flag = Flag(o_alu, uM_o, name="O-Flag") - l_flag = Flag(l_lc, uM_l, name="L-Flag") + z_flag = IntegerRegister(z_alu, uM_z, bit_length=1, name="Z-Flag") + n_flag = IntegerRegister(n_alu, uM_n, bit_length=1, name="N-Flag") + c_flag = IntegerRegister(c_alu, uM_c, bit_length=1, name="C-Flag") + o_flag = IntegerRegister(o_alu, uM_o, bit_length=1, name="O-Flag") + l_flag = IntegerRegister(l_lc, uM_l, bit_length=1, name="L-Flag") # Bus bus = Bus( @@ -340,6 +338,7 @@ class MIA_CPU(Processor): ar = self.get_module("AR") hr = self.get_module("HR") grx = self.get_module("GRx") + lc = self.get_module("LC") bus_signal_pairs = [ (asr.signals["in_content"], None), @@ -377,6 +376,8 @@ class MIA_CPU(Processor): widget = FlagGraphicsItem(module) gui.add_module_graphics_item(widget) + gui.add_signal_viewer(lc.signals["out_flag_l"], "L") + memory_modules = ["PM", "K1", "K2"] for name in memory_modules: module = self.get_module(name) diff --git a/src/simudator/processor/mia/modules/ar.py b/src/simudator/processor/mia/modules/ar.py index 5ca55569f7678c4a57cc67b5adb8a4f8090a0129..eb05d1753b8191e6e83e2f17eec86785f6608af6 100644 --- a/src/simudator/processor/mia/modules/ar.py +++ b/src/simudator/processor/mia/modules/ar.py @@ -1,4 +1,4 @@ -from simudator.core.modules.register import IntegerRegister +from simudator.core.modules.integer_register import IntegerRegister from simudator.core.signal import Signal from simudator.processor.mia.modules.mia_bus_connect import MiaBusConnector diff --git a/src/simudator/processor/mia/modules/asr.py b/src/simudator/processor/mia/modules/asr.py index 52db394554dbaf045e44ca106ad3df0a7af6b361..3afc91c302563a38724033fb198b98d49fa1403a 100644 --- a/src/simudator/processor/mia/modules/asr.py +++ b/src/simudator/processor/mia/modules/asr.py @@ -1,4 +1,4 @@ -from simudator.core.modules.register import IntegerRegister +from simudator.core.modules.integer_register import IntegerRegister from simudator.core.signal import Signal from simudator.processor.mia.modules.mia_bus_connect import MiaBusConnector diff --git a/src/simudator/processor/mia/modules/hr.py b/src/simudator/processor/mia/modules/hr.py index 2f8381c9b5f1dcba155b445e86d75e25ec163005..107f75bd06c7af9c8c69092e5abc70aec6dc8645 100644 --- a/src/simudator/processor/mia/modules/hr.py +++ b/src/simudator/processor/mia/modules/hr.py @@ -1,4 +1,4 @@ -from simudator.core.modules.register import IntegerRegister +from simudator.core.modules.integer_register import IntegerRegister from simudator.core.signal import Signal from simudator.processor.mia.modules.mia_bus_connect import MiaBusConnector diff --git a/src/simudator/processor/mia/modules/lc.py b/src/simudator/processor/mia/modules/lc.py index 9380ae688e14165de2968910e593131938b10ef1..0a18c2acaadb02dfcfe41347da111791c34d3381 100644 --- a/src/simudator/processor/mia/modules/lc.py +++ b/src/simudator/processor/mia/modules/lc.py @@ -10,16 +10,44 @@ class LC(Module): """A class representing the loop counter. It is controlled by the signal uM_control which determines if it should read from bus, increase by one or read from mM_uADR. + + Parameters + ---------- + mM_control : Signal + A signal connection from the micro memory + to the loop counter. This allows the micro memory to send bit + 12 and 13 to the loop counter. Bit 12 and 13 decides the + loop counters behaviour. + bus_input : Signal + A signal connection from the bus to the loop + counter. The loop counter reads from this signal when it reads + from the bus. + l_flag : Signal + A signal connection from the loop counter to the + L-flag. The loop counter writes to this signal when it needs to + update the L-flag. + mM_uADR : Signal + A signal connection from the micro memory to + the loop counter. This allows the loop counter to read the 7 + least significant bits from the uADR field. + name : str + Optional name of the loop counter. + value : int + Optional start value of the loop counter. """ __slots__ = ( "_value", - "_read_from_bus", - "_read_from_uADR", - "_decrement_by_one", + "_set_l_flag", "_bit_length", ) + BIT_LENGTH = 8 + """ + Number of bits used to store the loop counter value. Used to truncate too + large input. + """ + def __init__( self, mM_control: Signal, @@ -28,35 +56,7 @@ class LC(Module): mM_uADR: Signal, name="LC", value=0, - bit_length=8, ) -> None: - """ - Parameters - ---------- - mM_control : Signal - A signal connection from the micro memory - to the loop counter. This allows the micro memory to send bit - 12 and 13 to the loop counter. Bit 12 and 13 decides the - loop counters behaviour. - bus_input : Signal - A signal connection from the bus to the loop - counter. The loop counter reads from this signal when it reads - from the bus. - l_flag : Signal - A signal connection from the loop counter to the - L flag. The loop counter writes to this signal when it needs to - update the L flag. - mM_uADR : Signal - A signal connection from the micro memory to - the loop counter. This allows the loop counter to read the 7 - least significant bits from the uADR field. - name : str - Optional name of the loop counter. - value : int - Optional start value of the loop counter. - bit_length : int - Optional bit length of the loop counter. - """ # signals signals = { @@ -70,93 +70,65 @@ class LC(Module): # the value of the loop counter self._value = value + self._bit_length = self.BIT_LENGTH - # helper variables - self._read_from_bus = False - self._read_from_uADR = False - self._decrement_by_one = False - - # bit length and mask - self._bit_length = bit_length + # helper to correctly set and reset the L-flag + self._set_l_flag = False def update_register(self) -> None: - """Reads bit 12 and 13 from the micro memory and updates the - loop counter. - 0 - does nothing. - 1 - decrements the loop counter by one. - 2 - loads the 8 least significant bits from the bus. - 3 - loads the 7 least significant bits from the uADR field. + """Read bit 12 and 13 from the micro memory and update the + loop counter accordingly. + + 0 - do nothing. + 1 - decrement the loop counter by one. + 2 - load the 8 least significant bits from the bus. + 3 - load the 7 least significant bits from the uADR field. """ + bit_mask = 2**self._bit_length - 1 match self.signals["in_control"].get_value(): - case 0b00: # LC is not effected - self._decrement_by_one = False - self._read_from_bus = False - self._read_from_uADR = False + case 0b00: # Do nothing + return case 0b01: # Decrement by one - self._decrement_by_one = True - self._read_from_bus = False - self._read_from_uADR = False - - case 0b10: # Load 8 least significant bits from bus - self._decrement_by_one = False - self._read_from_bus = True - self._read_from_uADR = False - - case 0b11: # LCs 7 least significant bits are loaded from uADR - self._decrement_by_one = False - self._read_from_bus = False - self._read_from_uADR = True + self._value -= 1 - if self._read_from_bus: - input_value = self.signals["in_input"].get_value() - mask = 2**self._bit_length - 1 - self._value = input_value & mask + if self._value < 0: + # underflow correctly + # (the bit mask is the same as the maximum value) + self._value = bit_mask - if self._read_from_uADR: - input_value = self.signals["in_address"].get_value() - mask = 2**self._bit_length - 1 - self._value = input_value & mask + case 0b10: # Load 8 least significant bits from bus + self._value = self.signals["in_input"].get_value() - if self._decrement_by_one: - self._value -= 1 + case 0b11: # Load 7 least significant bits from uADR + self._value = self.signals["in_address"].get_value() - # underflow correctly - if self._value < 0: - mask = 2**self._bit_length - 1 - self._value = mask + # Truncate if the value is too large and determine if the L-flag + # should be set + self._value = self._value & bit_mask + self._set_l_flag = self._value == 0 def output_register(self) -> None: - """The loop counter will only output to the L flag, this is - handled in 'update_logic'. + """Set the L-flag to 1 if the loop counter has reached zero. Set the + L-flag to 0 if the loop counter is not zero. """ - pass - - def update_logic(self) -> None: - """When the loop counter reaches zero, set the l flag to 1. - Otherwise set it to zero. - """ - if self._value == 0: + if self._set_l_flag: self.signals["out_flag_l"].update_value(1) else: self.signals["out_flag_l"].update_value(0) - def get_state(self) -> dict[str, Any]: - """Returns a dict of the loop counter state. - These states are changable via set_states. + def update_logic(self) -> None: + """Do nothing. - Returns - ------- - dict[Any] - The state of the loop counter. + The loop counter has no internal logic. """ + pass + + def get_state(self) -> dict[str, Any]: state = dict() state["name"] = self.name state["value"] = self._value - state["read_from_bus"] = self._read_from_bus - state["read_from_uADR"] = self._read_from_uADR - state["decrement_by_one"] = self._decrement_by_one return state def get_parameter(self) -> dict[str, Any]: @@ -173,24 +145,12 @@ class LC(Module): return parameter def set_state(self, state: dict[str, Any]) -> None: - """Sets the loop counter state to one given in dict.""" self.name = state["name"] self._value = state["value"] - if "bit_length" in state: - self._bit_length = state["bit_length"] - if "read_from_bus" in state: - self._read_from_bus = state["read_from_bus"] - if "read_from_uADR" in state: - self._read_from_uADR = state["read_from_uADR"] - if "decrement_by_one" in state: - self._decrement_by_one = state["decrement_by_one"] def reset(self) -> None: - """Resets the loop counter to 0.""" + """Reset the loop counter to 0.""" self._value = 0 - self._read_from_bus = False - self._read_from_uADR = False - self._decrement_by_one = False def save_state_to_file(self, file_path: str) -> bool: content = self.name + ":\n" @@ -208,10 +168,4 @@ class LC(Module): "\n -----", "\n value: ", hex(self._value), - "\n decrement: ", - self._decrement_by_one, - "\n read from uADR: ", - self._read_from_uADR, - "\n read from bus: ", - self._read_from_bus, ) diff --git a/test/test_mia/test_lc.py b/test/test_mia/test_lc.py index 26e1bea3d15d6d8856c2525a2c3e7fa8753e1bbb..3eef1109e533f664287eaf3b88e7080d8142ff6e 100644 --- a/test/test_mia/test_lc.py +++ b/test/test_mia/test_lc.py @@ -1,6 +1,5 @@ from unittest.mock import mock_open, patch -from simudator.core.modules import Flag from simudator.core.processor import Processor from simudator.core.signal import Signal from simudator.processor.mia.modules.lc import LC @@ -30,11 +29,37 @@ def test_read_from_bus(): cpu.add_module(lc) - bus_input_s.update_value(10) + # Set the LC to read from the bus mM_control_s.update_value(2) + # Reading a non-zero value should set the L-flag to 0 + bus_input_s.update_value(10) cpu.do_tick() assert lc._value == 10 + assert l_flag_s.get_value() == 0 + + # Reading a zero while having a non-zero value should set the L-flag to 1 + bus_input_s.update_value(0) + cpu.do_tick() + assert lc._value == 0 + assert l_flag_s.get_value() == 1 + + # Reading a zero again should not reset the L-flag + cpu.do_tick() + assert lc._value == 0 + assert l_flag_s.get_value() == 1 + + # Reading a non-zero value now should reset the L-flag + bus_input_s.update_value(2) + cpu.do_tick() + assert lc._value == 2 + assert l_flag_s.get_value() == 0 + + # Check that the inputted value is correctly truncated + bus_input_s.update_value(0xFF1A) + cpu.do_tick() + assert lc._value == 0x1A + assert l_flag_s.get_value() == 0 def test_read_from_uADR(): @@ -47,72 +72,72 @@ def test_read_from_uADR(): cpu.add_module(lc) - mM_uADR_s.update_value(10) - mM_control_s.update_value(3) + # Set the LC to read from uADR + mM_control_s.update_value(0b11) + # Reading a non-zero value should set the L-flag to 0 + mM_uADR_s.update_value(10) cpu.do_tick() assert lc._value == 10 + assert l_flag_s.get_value() == 0 + + # Reading a zero while having a non-zero value should set the L-flag to 1 + mM_uADR_s.update_value(0) + cpu.do_tick() + assert lc._value == 0 + assert l_flag_s.get_value() == 1 + + # Reading a zero again should not reset the L-flag + cpu.do_tick() + assert lc._value == 0 + assert l_flag_s.get_value() == 1 + # Reading a non-zero value now should reset the L-flag + mM_uADR_s.update_value(2) + cpu.do_tick() + assert lc._value == 2 + assert l_flag_s.get_value() == 0 + + # Check that the inputted value is correctly truncated + mM_uADR_s.update_value(0xFF1A) + cpu.do_tick() + assert lc._value == 0x1A + assert l_flag_s.get_value() == 0 -def test_write_to_l_flag(): + +def test_decrement(): cpu = Processor() # signals needed mM_control_s = Signal(cpu) mM_uADR_s = Signal(cpu) l_flag_s = Signal(cpu) - l_flag_output_s = Signal(cpu) bus_input_s = Signal(cpu) # modules needed to run the test # initialize loop counter to 1, so when it is decremented by - # one it reaches zero and sets the L flag - # initialize the L flag as a register with 1 bit length and - # the value 0 + # one it reaches zero and sets the L-flag lc_value = 1 - l_bit_length = 1 - l_value = 0 lc = LC(mM_control_s, bus_input_s, l_flag_s, mM_uADR_s, "lc", lc_value) - l_flag = Flag(l_flag_s, l_flag_output_s, l_bit_length, l_value) # add modules to the processor cpu.add_module(lc) - cpu.add_module(l_flag) # update the value of the control signal and the micro controller mM_uADR_s.update_value(0) mM_control_s.update_value(0b01) # The loop counter will now change its value from 1 to 0 which - # should set the L flag to 1 in the same cycle + # should set the L-flag to 1 in the same cycle cpu.do_tick() - assert l_flag._value == 1 - - -def test_reset_l_flag(): - cpu = Processor() - - mM_control_s = Signal(cpu) - mM_uADR_s = Signal(cpu) - l_flag_s = Signal(cpu) - l_flag_output_s = Signal(cpu) - bus_input_s = Signal(cpu) - - lc = LC(mM_control_s, bus_input_s, l_flag_s, mM_uADR_s, "lc", 1) - l_flag = Flag(l_flag_s, l_flag_output_s, 8) - - cpu.add_module(lc) - cpu.add_module(l_flag) - - mM_uADR_s.update_value(0) - mM_control_s.update_value(3) + assert lc._value == 0 + assert l_flag_s.get_value() == 1 + # Decrementing again should make the LC wrap around to 0xFF and reset the + # L-flag cpu.do_tick() - - mM_uADR_s.update_value(10) - - cpu.do_tick() - assert l_flag._value == 0 + assert lc._value == 0xFF + assert l_flag_s.get_value() == 0 def test_lc_do_nothing(): @@ -132,12 +157,28 @@ def test_lc_do_nothing(): cpu.do_tick() assert lc._value == 3 + assert l_flag_s.get_value() == 0 # tell lc to do nothing mM_control_s.update_value(0b00) cpu.do_tick() assert lc._value == 3 + assert l_flag_s.get_value() == 0 + + # decrement to zero and then do nothing to check that the L-flag remains + # set to 1 + mM_control_s.update_value(0b01) + cpu.do_tick() + cpu.do_tick() + cpu.do_tick() + assert lc._value == 0 + assert l_flag_s.get_value() == 1 + + mM_control_s.update_value(0b00) + cpu.do_tick() + assert lc._value == 0 + assert l_flag_s.get_value() == 1 def test_get_state(): @@ -159,9 +200,6 @@ def test_get_state(): state = lc.get_state() assert state["name"] == "LC" assert state["value"] == 255 - assert state["read_from_bus"] is False - assert state["read_from_uADR"] is False - assert state["decrement_by_one"] is True mM_control_s.update_value(0b10) cpu.do_tick() @@ -169,9 +207,6 @@ def test_get_state(): state = lc.get_state() assert state["name"] == "LC" assert state["value"] == 100 - assert state["read_from_bus"] is True - assert state["read_from_uADR"] is False - assert state["decrement_by_one"] is False mM_control_s.update_value(0b11) cpu.do_tick() @@ -179,9 +214,6 @@ def test_get_state(): state = lc.get_state() assert state["name"] == "LC" assert state["value"] == 10 - assert state["read_from_bus"] is False - assert state["read_from_uADR"] is True - assert state["decrement_by_one"] is False def test_get_parameter():