Skip to content
Snippets Groups Projects
Commit c5b3f604 authored by Johannes Kung's avatar Johannes Kung
Browse files

Refactored docstrings of processor class

parent 69f93949
No related branches found
No related tags found
1 merge request!27Docstring refactor of core
Pipeline #132095 failed
...@@ -116,34 +116,54 @@ class Processor: ...@@ -116,34 +116,54 @@ class Processor:
Each entry in the list is a tuple of a instruction with its column and Each entry in the list is a tuple of a instruction with its column and
row position for the pipeline diagram. row position for the pipeline diagram.
Returns
-------
list[tuple[str, int, int]]
A list of instructions represented as tuples. Each tuple contains
the instruction and its position in the pipeline.
""" """
raise NotImplementedError raise NotImplementedError
def get_pipeline_dimensions(self) -> tuple[int, int]: def get_pipeline_dimensions(self) -> tuple[int, int]:
"""Return a tuple on the form (x, y) for the processors preferred """Return the dimensions of the pipeline of the processor.
pipeline display size.
(x, y) represents the number of columns respective rows the pipeline Returns
should have. -------
width : int
The width of the pipeline (number of cycles to display at a time).
depth : int
Depth of the pipeline.
""" """
raise NotImplementedError raise NotImplementedError
def get_asm_instruction(self) -> str: def get_asm_instruction(self) -> str:
""" """Return a string containing the 'code' for the current assembly
Return a string containing the 'code' for the current assembly
instruction. instruction.
What 'code' refers to is up to each processor to define. It could be a What 'code' refers to is up to each processor to define. It could be a
memory address, an instruction label or something else. memory address, an instruction label or something else.
Returns
-------
str
The current assembly instruction.
""" """
raise NotImplementedError raise NotImplementedError
def run_asm_instruction(self, num_instructions=1) -> None: def run_asm_instruction(self, num_instructions=1) -> None:
""" """Run a number of assembly instructions.
Runs assembler instructions on the processors 'num_instructions' times.
Default argument is one, but it is possible to specify any number of Defaults to running 1 instruction unless some other number is provided.
instructions. This should be implemented per processor and thus we
Parameters
----------
num_instructions : int
Number of assembly instructions to perform.
Notes
-----
This should be implemented per processor and thus we
raise NotImplementedError. raise NotImplementedError.
""" """
...@@ -151,14 +171,15 @@ class Processor: ...@@ -151,14 +171,15 @@ class Processor:
def undo_asm_instruction(self, num_instructions=1) -> None: def undo_asm_instruction(self, num_instructions=1) -> None:
""" """
Undos 'num_instructions' numbers of assembler instructions on the CPU. Undo a number of assembly instructions on the CPU.
Default argument is one, but it is possible to specify any number of Default argument is one, but it is possible to specify any number of
instructions. Undo assembler instruction assumes the CPU is always at instructions.
the end of the list 'self.assembly_cycles'.
Undos asm instructions by finding the corresponding clock cycle before Parameters
said number of asm instructions started and loading that clock cycle. ----------
num_instructions : int
Number of assembly instructions to undo.
""" """
current_clock_cycle = self.clock current_clock_cycle = self.clock
...@@ -196,7 +217,12 @@ class Processor: ...@@ -196,7 +217,12 @@ class Processor:
def should_halt(self) -> bool: def should_halt(self) -> bool:
""" """
Return True when the processor should halt, otherwise False. Return whether the processor should halt this clock cycle or not.
Returns
-------
bool
``True`` if the processor should halt, ``False`` otherwise.
Notes Notes
---- ----
...@@ -208,16 +234,13 @@ class Processor: ...@@ -208,16 +234,13 @@ class Processor:
return False return False
def stop(self) -> None: def stop(self) -> None:
""" """Signal to stop the execution of the processor until the processor is
Signal to stop the execution of the processor until the processor is
instructed to run continuously or do some ticks again. instructed to run continuously or do some ticks again.
""" """
self.is_stopped = True self.is_stopped = True
def unstop(self) -> None: def unstop(self) -> None:
""" """Reset the stop execution signal."""
Reset the stop execution signal.
"""
self.is_stopped = False self.is_stopped = False
def is_new_instruction(self) -> bool: def is_new_instruction(self) -> bool:
...@@ -267,45 +290,83 @@ class Processor: ...@@ -267,45 +290,83 @@ class Processor:
module.update_logic() module.update_logic()
def add_modules_to_update(self, module: Module) -> None: def add_modules_to_update(self, module: Module) -> None:
""" """Queue a module to be updated at the end of clock cycle.
Queues module to be updated at the end of clock cycle.
Parameters
----------
module : Module
Module to queue for update.
See Also
--------
Module.update_logic :
Method for updating a module.
""" """
if module not in self.update_queue: if module not in self.update_queue:
self.update_queue.append(module) self.update_queue.append(module)
def add_module(self, module: Module) -> None: def add_module(self, module: Module) -> None:
""" """Add module to be simulated by the processor.
Add module into the processor.
Parameters
----------
module : Module
Module to add.
""" """
self.modules[module.name] = module self.modules[module.name] = module
def get_module(self, name: str) -> Module: def get_module(self, name: str) -> Module:
""" """Get module with specific name.
Get module with specific name.
Parameters
----------
name : str
Name of the module to get.
Returns
-------
Module
The module with the specified name.
""" """
return self.modules[name] return self.modules[name]
def get_modules(self) -> list[Module]: def get_modules(self) -> list[Module]:
""" """Get list of all modules.
Get list of all modules.
Returns
-------
list[Module]
List of all modules in the processor.
""" """
return list(self.modules.values()) return list(self.modules.values())
def add_signals(self, signals: list[Signal]) -> None: def add_signals(self, signals: list[Signal]) -> None:
""" """Add signals to the processor.
Add signals into the processor.
Parameters
----------
list[Signal]
List of signals to add for simulation.
""" """
self.signals += signals self.signals += signals
def get_clock(self) -> int: def get_clock(self) -> int:
""" """Get the current clockcycle number.
Get current clockcycle.
Returns
-------
int
Current clock cycle number of the processor.
""" """
return self.clock return self.clock
def set_clock(self, value: int) -> None: def set_clock(self, value: int) -> None:
""" """Set current clockcycle number.
Set current clockcycle.
Parameters
----------
value : int
Cycle number to set the clock to.
""" """
self.clock = value self.clock = value
...@@ -327,10 +388,15 @@ class Processor: ...@@ -327,10 +388,15 @@ class Processor:
self.module_history.append(module_states) self.module_history.append(module_states)
def load_cycle(self, cycle: int) -> None: def load_cycle(self, cycle: int) -> None:
""" """Load the state of all modules as they were at the specified clock
Load the state of all modules as they were at the specified clock cycle. cycle.
This does not erase the history of states for the later cycles. This does not erase the history of states for the later cycles.
Parameters
----------
cycle : int
Number of the cycle to load.
""" """
cycle_index = cycle - self.removed_cycles cycle_index = cycle - self.removed_cycles
...@@ -355,13 +421,13 @@ class Processor: ...@@ -355,13 +421,13 @@ class Processor:
module = self.update_queue.pop(0) module = self.update_queue.pop(0)
module.update_logic() module.update_logic()
def load_state_from_file(self, file_path) -> None: def load_state_from_file(self, file_path: str) -> None:
""" """Load states for modules from a file.
Loads states for modules from a file.
Appends the lines from the loaded file to a long string. This string Parameters
is given to each module in the function 'load_from_str'. Each module ----------
will thus load itself. file_path : str
Path to the file containing the processor state to load.
""" """
self.reset() self.reset()
...@@ -374,6 +440,9 @@ class Processor: ...@@ -374,6 +440,9 @@ class Processor:
else: else:
raise ValueError # Else raise error raise ValueError # Else raise error
# Appends the lines from the loaded file to a long string. This string
# is given to each module in the function 'load_from_str'. Each module
# will thus load itself.
module_name = None module_name = None
module_str = "" module_str = ""
...@@ -405,10 +474,14 @@ class Processor: ...@@ -405,10 +474,14 @@ class Processor:
module.load_from_str(module_str) module.load_from_str(module_str)
def save_state_to_file(self, file_path: str) -> None: def save_state_to_file(self, file_path: str) -> None:
""" """Save the current state of the cpu to file.
Saves the current state of the cpu to a given file.
Will overwrite the given file. Will overwrite the given file.
Parameters
----------
file_path : str
Path to the file to write to.
""" """
file = open(file_path, "w") file = open(file_path, "w")
file.write("") file.write("")
...@@ -419,13 +492,21 @@ class Processor: ...@@ -419,13 +492,21 @@ class Processor:
module.save_state_to_file(file_path) module.save_state_to_file(file_path)
def pretty_print(self) -> None: def pretty_print(self) -> None:
"""Print the processor state in a readable and compact format."""
self.pretty_print_verbose(self.ignore_keys) self.pretty_print_verbose(self.ignore_keys)
def pretty_print_verbose(self, ignore_keys=None) -> None: def pretty_print_verbose(self, ignore_keys=None) -> None:
""" """
Prints the most relevant information about each module Print the most relevant information about each module in a compact and
compact and pretty. Fields of modules can be ignored with the readable format.
optional argument 'ignore_keys'.
State variables of modules can be ignored with the optional argument.
Parameters
----------
ignore_keys : list[str]
List of names of state variables of modules to exclude when
printing module states.
""" """
# TODO: ignore keys per module and not for all modules # TODO: ignore keys per module and not for all modules
memory_modules = [] memory_modules = []
...@@ -455,7 +536,8 @@ class Processor: ...@@ -455,7 +536,8 @@ class Processor:
module_to_line_length = {} module_to_line_length = {}
# get the longest line length for all other_modules # get the longest line length for all other_modules
for module in other_modules: for module in other_modules:
line_len = module.get_longest_line_len(ignore_keys) + 3 # +3 for padding # +3 for padding
line_len = module.get_longest_line_len(ignore_keys) + 3
# TODO: what to do if two or more modules has the same name # TODO: what to do if two or more modules has the same name
module_to_line_length[module] = line_len module_to_line_length[module] = line_len
...@@ -464,7 +546,7 @@ class Processor: ...@@ -464,7 +546,7 @@ class Processor:
print(Processor.LINE_SEPARATOR * Processor.MAX_LINE_LEN) print(Processor.LINE_SEPARATOR * Processor.MAX_LINE_LEN)
# print each group seperate # print each group separate
for group in groups: for group in groups:
self.pretty_print_names(group) self.pretty_print_names(group)
...@@ -493,8 +575,7 @@ class Processor: ...@@ -493,8 +575,7 @@ class Processor:
real_keys[row] + ": " + str(module_state[real_keys[row]]) real_keys[row] + ": " + str(module_state[real_keys[row]])
) )
# padd the string so each string has the same # pad the string so each string has the same length
# length
total_padding += module_to_line_length[module] total_padding += module_to_line_length[module]
string = string.ljust(total_padding) string = string.ljust(total_padding)
# replace last to chars with a separator and padding # replace last to chars with a separator and padding
...@@ -506,7 +587,14 @@ class Processor: ...@@ -506,7 +587,14 @@ class Processor:
for memory_module in memory_modules: for memory_module in memory_modules:
self.pretty_print_memory(memory_module) self.pretty_print_memory(memory_module)
def pretty_print_memory(self, module: Module) -> None: def pretty_print_memory(self, module: Memory) -> None:
"""Print a memory module in a compact and readable format.
Parameters
----------
module : Memory
Memory module to print the state of.
"""
print(module.name) print(module.name)
print(Processor.LINE_SEPARATOR * Processor.MAX_LINE_LEN) print(Processor.LINE_SEPARATOR * Processor.MAX_LINE_LEN)
...@@ -515,11 +603,11 @@ class Processor: ...@@ -515,11 +603,11 @@ class Processor:
string = "" string = ""
last_mem_len = module.get_largest_mem_adr() last_mem_len = module.get_largest_mem_adr()
for i, value in enumerate(module.memory): for i, value in enumerate(module.get_state()["memory"]):
# create a new string containg the adress and the value # create a new string containing the address and the value
# of the memory adress formattet to fit with the largest # of the memory address formatted to fit with the largest
# adress and largest memory value # address and largest memory value
new_row = (str(i).ljust(last_mem_len) + ": " + str(value)).ljust( new_row = (str(i).ljust(last_mem_len) + ": " + str(value)).ljust(
longest_line_len + last_mem_len + 3 longest_line_len + last_mem_len + 3
) + "|" ) + "|"
...@@ -542,11 +630,16 @@ class Processor: ...@@ -542,11 +630,16 @@ class Processor:
def pretty_print_names(self, module_to_line_length: dict[Module, int]) -> None: def pretty_print_names(self, module_to_line_length: dict[Module, int]) -> None:
""" """
Prints the name of the modules in one row, but pretty. Print the name of the modules in one row with formatting.
Adds spacing between the names so that the longest state variable of
each module has space to be printed below the name.
Adds spacing between the names so that the modules Parameters
longest field will have space to be printed below. ----------
module_to_line_length : dict[Module, int]
Mapping from module to length of the longest state variable of
the module.
""" """
name_string = "" name_string = ""
total_len = 0 total_len = 0
...@@ -561,9 +654,20 @@ class Processor: ...@@ -561,9 +654,20 @@ class Processor:
def group_pp_modules( def group_pp_modules(
self, module_to_line_length: dict[Module, int] self, module_to_line_length: dict[Module, int]
) -> list[dict[Module, int]]: ) -> list[dict[Module, int]]:
""" """Group the modules to be pretty printed into groups with a
Groups the modules to be pretty printed into groups with a
total line length lower than 'Processor.MAX_LINE_LEN'. total line length lower than 'Processor.MAX_LINE_LEN'.
Parameters
----------
module_to_line_length : dict[Module, int]
Mapping from module to length of the longest state variable of
the module (takes as the line length).
Returns
-------
list[dict[Module, int]]
List of mappings from module to line length, each mapping
representing a group of modules for printing.
""" """
groups = [{}] groups = [{}]
...@@ -586,8 +690,21 @@ class Processor: ...@@ -586,8 +690,21 @@ class Processor:
def fields_to_list(self, module: Module, ignore_keys=[]) -> list[str]: def fields_to_list(self, module: Module, ignore_keys=[]) -> list[str]:
""" """
Return a list containing all the keys for a module except the name. Return a list containing all state variable names, excluding module
Optinal argument to ignore specific keys. name, for a module.
Optional argument to ignore specific state variables.
Parameters
----------
module : Module
Module to get state variable names from.
ignore_keys : list[str]
List of state variable names to exclude.
Returns
-------
list[str]
List of state variable names, excluding module name, of a module.
""" """
return [ return [
key key
...@@ -595,11 +712,24 @@ class Processor: ...@@ -595,11 +712,24 @@ class Processor:
if key != "name" and key not in ignore_keys if key != "name" and key not in ignore_keys
] ]
def get_most_fields(self, modules: dict[Module,int], ignore_keys=[]) -> int: def get_most_fields(self, modules: dict[Module, int], ignore_keys=[]) -> int:
""" """Get the maximum number of state variables among all modules.
Return the most number of a fields a module has.
Can optionally ignore keys. Can optionally ignore keys.
Parameters
----------
modules : dict[Module, int]
Mapping from module to length of the longest state variable of
the module. Used as a list of modules to take the maximum over.
ignore_keys : list[str]
State variables to exclude.
Returns
-------
int
The maximum of the number of module state variables among all
modules of the processor.
""" """
fields = 0 fields = 0
for module in modules: for module in modules:
...@@ -612,6 +742,24 @@ class Processor: ...@@ -612,6 +742,24 @@ class Processor:
def add_state_breakpoint( def add_state_breakpoint(
self, module_name: str, state_name: str, value: Any self, module_name: str, state_name: str, value: Any
) -> None: ) -> None:
"""Add a module state breakpoint to the processor.
Parameters
----------
module_name : str
Name of the module for which a breakpoint will be added.
state_name : str
Name of the state variable of the module to put a breakpoint
on.
value : Any
Value of the state variable that triggers the breakpoint.
Raises
------
ValueError
If the module does not exist or if the state variable does not
exist in the module.
"""
if module_name not in self.modules: if module_name not in self.modules:
raise ValueError(f"No module named {module_name}") raise ValueError(f"No module named {module_name}")
...@@ -626,19 +774,43 @@ class Processor: ...@@ -626,19 +774,43 @@ class Processor:
self.breakpoint_id_counter += 1 self.breakpoint_id_counter += 1
def remove_breakpoint(self, id: int) -> bool: def remove_breakpoint(self, id: int) -> bool:
"""Remove a specific breakpoint.
Parameters
----------
id : int
ID of the breakpoint to be removed.
"""
if id in self.breakpoints: if id in self.breakpoints:
del self.breakpoints[id] del self.breakpoints[id]
return True return True
return False return False
def print_breakpoints(self) -> None: def print_breakpoints(self) -> None:
"""Print all breakpoints of the processor."""
if not self.breakpoints: if not self.breakpoints:
print("There are no breakpoints.") print("There are no breakpoints.")
else: else:
for bp_id, bp in self.breakpoints.items(): for bp_id, bp in self.breakpoints.items():
print(f"BP {bp_id}: {bp}") print(f"BP {bp_id}: {bp}")
def add_memory_breakpoint(self, module_name: str, adress: int, value: Any) -> None: def add_memory_breakpoint(self, module_name: str, address: int, value: Any) -> None:
"""Add a breakpoint for a memory module.
Parameters
----------
module_name : str
Name of the memory module for which a breakpoint will be added.
address : int
Address of the memory module to break at.
value : Any
Value of the address that triggers the breakpoint.
Raises
------
ValueError
If the module does not exist or is not a memory.
"""
if module_name not in self.modules: if module_name not in self.modules:
raise ValueError(f"No module named {module_name}") raise ValueError(f"No module named {module_name}")
...@@ -647,14 +819,42 @@ class Processor: ...@@ -647,14 +819,42 @@ class Processor:
if "memory" not in module.get_state(): if "memory" not in module.get_state():
raise ValueError(f"Module {module_name} is not a memory.") raise ValueError(f"Module {module_name} is not a memory.")
bp = MemoryBreakpoint(module, adress, value) bp = MemoryBreakpoint(module, address, value)
self.breakpoints[self.breakpoint_id_counter] = bp self.breakpoints[self.breakpoint_id_counter] = bp
self.breakpoint_id_counter += 1 self.breakpoint_id_counter += 1
def get_breakpoint_lambdas(self) -> list[str]: def get_breakpoint_lambdas(self) -> list[str]:
"""Get all functions available for lambda breakpoints.
Returns
-------
list[str]
List of names of all functions of the processor that are available
for adding lambda breakpoints.
"""
return list(self.lambdas.keys()) return list(self.lambdas.keys())
def add_lambda_breakpoint(self, lambda_name: str, **kwargs) -> None: def add_lambda_breakpoint(self, lambda_name: str, **kwargs) -> None:
"""Add a lambda breakpoint to the processor.
Module names used in the keyword arguments are substituted by the
actual modules before being passed on to the lambda breakpoint
function.
Parameters
----------
lambda_name : str
Name of the lambda breakpoint function to be used by the
breakpoint.
**kwargs
Arguments to pass to the breakpoint function.
Raises
------
ValueError
If the lambda breakpoint function does not exist.
"""
# Substitute any module names for the module instances # Substitute any module names for the module instances
# This is to be able to externally create general lambda breakpoints # This is to be able to externally create general lambda breakpoints
# that can take any values as arguments while also having a way to take # that can take any values as arguments while also having a way to take
...@@ -673,4 +873,13 @@ class Processor: ...@@ -673,4 +873,13 @@ class Processor:
self.breakpoint_id_counter += 1 self.breakpoint_id_counter += 1
def set_enabled_breakpoint(self, bp_id: int, is_enabled: bool) -> None: def set_enabled_breakpoint(self, bp_id: int, is_enabled: bool) -> None:
"""Toggle a breakpoint to enabled or disabled.
Parameters
----------
bp_id : int
ID of the breakpoint to toggle.
is_enabled : bool
``True`` to enable, ``False`` to disable the breakpoint.
"""
self.breakpoints[bp_id].set_enabled(is_enabled) self.breakpoints[bp_id].set_enabled(is_enabled)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment