Skip to content
Snippets Groups Projects
Commit af4a5d7b authored by Martin Högstedt's avatar Martin Högstedt
Browse files

merged with main

parents ef9bc4c2 19ae22d0
No related branches found
No related tags found
1 merge request!13We know have asm instructions, can step forward and backward between clock cycle and asm instructions
Pipeline #131553 failed
......@@ -73,6 +73,13 @@ class CLI:
case ["rc"] | ["run_continuously"]:
self.processor.run_continuously()
if self.processor.breakpoint_reached:
bp = self.processor.last_breakpoint
print(f"Reached breakpoint: {bp}")
if self.processor.should_halt():
print("The processor halted")
case ["r"] | ["resets"]:
# self.processor.load_cycle(0)
......@@ -86,6 +93,13 @@ class CLI:
try:
for _ in range(int(user_input.split()[1])):
self.processor.do_tick()
if self.processor.is_stopped:
break
if self.processor.breakpoint_reached:
bp = self.processor.last_breakpoint
print(f"Reached breakpoint: {bp}")
if self.processor.should_halt():
print("The processor halted")
except ValueError:
print("Invalid value")
......
......@@ -50,8 +50,10 @@ class Processor:
def do_tick(self) -> None:
"""
Does one clock-cycle of the processor by running each modules
tick function, then handels the following updates.
Simulate one clock cycle of the processor.
Run each module's tick function, then handle the following updates.
Also check for breakpoints that are reached in this cycle.
"""
if len(self.module_history) > self.clock - self.removed_cycles:
# If a previous stored cycle has been loaded, discard
......@@ -60,6 +62,7 @@ class Processor:
self.module_history = self.module_history[0:self.clock]
self.signal_history = self.signal_history[0:self.clock]
self.unstop()
self.save_cycle()
self.clock += 1
......@@ -73,6 +76,10 @@ class Processor:
module = self.update_queue.pop(0)
module.update_logic()
self.stop_at_breakpoints()
if self.should_halt():
self.stop()
def get_current_instrution(self) -> str:
"""
Return the current instruction. Useful for pipeline diagram.
......@@ -89,31 +96,28 @@ class Processor:
raise NotImplemented
def run_continuously(self):
def run_continuously(self) -> None:
"""
Runs the processor until it halts.
Run the processor until it halts, is stopped or reaches a breakpoint.
"""
while not self.should_halt() and not self.is_stopped:
self.unstop()
while not self.is_stopped:
self.do_tick()
# Print all reached breakpoints then exit the loop
self.breakpoint_reached = False
for _, bp in self.breakpoints.items():
#TODO: Can make this more efficient by only checking enabled breakpoints
if bp.is_break() and bp.is_enabled:
self.breakpoint_reached = True
self.last_breakpoint = bp
if self.breakpoint_reached:
return
def should_halt(self) -> bool:
return False
def stop(self) -> None:
"""
Signal to stop the execution of the processor until the processor is
instructed to run continuously or do some ticks again.
"""
self.is_stopped = True
def unstop(self) -> None:
"""
Reset the stop execution signal.
"""
self.is_stopped = False
def is_new_instruction(self) -> bool:
......@@ -125,7 +129,24 @@ class Processor:
"""
return self.new_instruction
def reset(self) -> None:
def stop_at_breakpoints(self) -> None:
"""
Stop the execution if any breakpoint has been reached during this cycle.
Also record the breakpoint that was reached.
"""
self.breakpoint_reached = False
for _, bp in self.breakpoints.items():
#TODO: Can make this more efficient by only checking enabled breakpoints
if bp.is_break() and bp.is_enabled:
self.breakpoint_reached = True
self.last_breakpoint = bp
if self.breakpoint_reached:
self.stop()
def reset(self):
"""
Resets all values in the processor and
then does one propogation to the signals.
......@@ -242,6 +263,8 @@ class Processor:
is given to each module in the function 'load_from_str'. Each module
will thus load itself.
"""
self.reset()
file = open(file_path)
# Ensure we are at the start of the file
......
......@@ -2,6 +2,7 @@ import ast
import json
import sys
from json import JSONDecodeError
from threading import Thread
from qtpy import QtCore, QtWidgets
from qtpy.QtCore import QPointF
......@@ -92,7 +93,7 @@ class GUI(QMainWindow):
and added individually.
"""
halted_signal = pyqtSignal(int)
cpu_tick_signal = pyqtSignal(int)
HALT_MESSAGE_THRESHOLD = 100
def __init__(self, cpu: Processor):
......@@ -116,7 +117,15 @@ class GUI(QMainWindow):
self.threadpool = QtCore.QThreadPool()
# Signal to tell gui when cpu has halted
self.halted_signal.connect(self.cpuHaltedFunction)
self.cpu_tick_signal.connect(self.handleCpuTick)
# Used to set if all values in the gui should be updated each tick
# or only the clock counter
self.update_all_values = False
# Used to set the update delay
# Useful when watching the values being updated while running
self.update_delay = 0.00
# Used to lock some actions in ui when cpu is running in another thread
# Using the cpu's internal status directly could case problems
......@@ -215,9 +224,22 @@ class GUI(QMainWindow):
self.breakpoint_action.setStatusTip("Open breakpoint window.")
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.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.triggered.connect(self.set_update_delay)
# Create Tools menu for tool actions actions
tools_menu = menu.addMenu("&Tools")
tools_menu.addAction(self.breakpoint_action)
tools_menu.addAction(self.update_value_action)
tools_menu.addAction(self.set_delay_action)
# Add run button on toolbar
arrow_icon = self.style().standardIcon(QStyle.SP_MediaPlay)
......@@ -333,14 +355,24 @@ class GUI(QMainWindow):
signal.connect(self.addLambdaBreakpoint)
case "UPDATE":
signal.connect(self.updateCpuListeners)
case "CLOCKUPDATE":
signal.connect(self.updateCpuClockCycle)
def updateCpuListeners(self) -> None:
"""
Updates the graphics items in the scene, the clock and the pipeline diagram.
Updates the graphics items in the scene and the clock.
Used after the cpu has run or when the user has edited somehting.
"""
self.cpu_graphics_scene.updateGraphicsItems()
self.updateCpuClockCycle()
def updateCpuClockCycle(self) -> None:
"""
Update the clock cycle counter.
Used while the program is running to show the user nothing has crashed.
"""
self.clock_label.setText("Clockcycle: " + str(self.cpu.get_clock()))
def lambdaBreakpointDialog(self) -> None:
......@@ -470,10 +502,8 @@ class GUI(QMainWindow):
steps = self.jump_value_box.value()
self.cpu_running = True
self.setDisabledWhenRunning(True)
self.cpu.unstop()
simultaion_thread = RunThread(self.cpu, self.halted_signal, False, steps)
self.threadpool.start(simultaion_thread)
self.updateCpuListeners()
simulation_thread = RunThread(self.cpu, self.cpu_tick_signal, self.update_delay, False, steps)
self.threadpool.start(simulation_thread)
def stepAsmToolBarButtonClick(self):
"""
......@@ -504,34 +534,43 @@ class GUI(QMainWindow):
# Create own thread for cpu simulation so gui dosent freeze
self.cpu_running = True
self.setDisabledWhenRunning(True)
self.cpu.unstop()
simultaion_thread = RunThread(self.cpu, self.halted_signal)
self.threadpool.start(simultaion_thread)
simulation_thread = RunThread(self.cpu, self.cpu_tick_signal, self.update_delay)
self.threadpool.start(simulation_thread)
@Slot(int)
def cpuHaltedFunction(self, steps: int) -> None:
def handleCpuTick(self, steps: int) -> None:
"""
Called from other thread when cpu has halted. Will inform the user and update visuals.
Called from other thread after every cpu tick.
Will inform the user and update visuals.
"""
# If a breakpoint halted the program inform thr user
if self.cpu.breakpoint_reached:
self.messageBox("Reached breakpoint: " + self.cpu.last_breakpoint.__str__())
# Only show halted message for larger steps that take time
# This is done so a user dosent have to close
# the message box after every small step
elif steps > self.HALT_MESSAGE_THRESHOLD:
self.messageBox("The processor halted.")
self.updateCpuListeners()
self.cpu_running = False
self.setDisabledWhenRunning(False)
# Update cpu clock counter every tick
self.updateCpuClockCycle()
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
# => Enable the relevant parts of the GUI again
if steps == 0:
self.cpu_running = False
self.setDisabledWhenRunning(False)
self.updateCpuListeners()
# Inform user of reached break point
if self.cpu.breakpoint_reached:
self.messageBox("Reached breakpoint: " + self.cpu.last_breakpoint.__str__())
# Inform user of halt
if self.cpu.should_halt():
self.messageBox("The processor halted.")
def stopToolBarButtonClick(self) -> None:
"""
Tells the cpu to stop. It will then stop at an appropriate in its own thread.
"""
self.cpu.stop()
self.updateCpuListeners()
# TODO: What is the difference between folderSaveDialog and folderLoadDialog?
......@@ -795,7 +834,21 @@ class GUI(QMainWindow):
self.breakpoint_window = BreakpointWindow(self.cpu)
self.breakpoint_window.show()
def showPortNamesBarButtonClick(self) -> None:
def toggle_value_update_on_run(self):
"""
Toggles whether all values or only clock cycle is being updated each tick.
"""
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)
if ok:
self.update_delay = delay
def showPortNamesBarButtonClick(self):
"""
Toggles showing port names in the graphics scene.
"""
......
from qtpy.QtCore import QRunnable
import time
from qtpy.QtCore import QRunnable
class RunThread(QRunnable):
"""
This class is used to run the cpu on a seperate thread.
This class is used to run the simulated cpu several ticks or continuously on
a seperate thread. This allows the user to interact with the GUI while the
simulation is running.
This allows the user to interact with the GUI will th cpu is running.
When the cpu halts this thread will emit to it's given signal
the GUI can then handel what should happend after execution on its own.
After each cpu tick, this thread will emit to its given QT signal so that
the GUI can update itself and possibly inform the user of when the execution
has halted.
"""
def __init__(self, cpu, signal, run_continuously=True, steps=0):
def __init__(self, cpu, signal, delay: float, run_continuously=True, steps=0):
super().__init__()
self.cpu = cpu
self.signal = signal
self.run_continuously = run_continuously
self.steps = steps
self.delay = delay
def run(self):
if self.run_continuously:
self.cpu.run_continuously()
self.cpu.unstop()
while not self.cpu.is_stopped:
self.cpu.do_tick()
self.signal.emit(1)
time.sleep(self.delay)
else:
for _ in range(self.steps):
self.cpu.do_tick()
self.signal.emit(1)
time.sleep(self.delay)
if self.cpu.is_stopped:
break
self.signal.emit(self.steps)
# Signal end of execution as having run 0 ticks
self.signal.emit(0)
def run_asm(self):
self.cpu.run_asm(self.steps)
self.signal.emit(self.steps)
self.signal.emit(0)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment