diff --git a/src/simudator/core/module.py b/src/simudator/core/module.py index c28dab000729b49c20f44bde1e8a60fb5a554ee2..66d73fa4ab445dccb0a7d07cb51cb54034fdd278 100644 --- a/src/simudator/core/module.py +++ b/src/simudator/core/module.py @@ -81,12 +81,15 @@ class Module: """ pass - def get_longest_line_len(self, ignore_keys=[]) -> int: + def get_longest_line_len(self, ignore_keys=None) -> int: """ Helper function for pretty_print that returns the length of the longest line to print for a module. """ + if ignore_keys == None: + ignore_keys = [] + longest_line_len = 0 state = self.get_state() diff --git a/src/simudator/core/processor.py b/src/simudator/core/processor.py index 876b62739d8b40e2257da72744252cddfea57eab..feb67121d09825d986a4e46a16addfc62e3bf78b 100644 --- a/src/simudator/core/processor.py +++ b/src/simudator/core/processor.py @@ -26,7 +26,7 @@ class Processor: self.clock = 0 self.update_queue = [] self.module_history: list[dict[str, dict[str, Any]]] = [] - self.signal_history: list[list] = [] # TODO: Is this needed? + self.signal_history: list[list] = [] # TODO: Is this needed? self.breakpoint_id_counter = 1 self.breakpoints: dict[int, Breakpoint] = {} self.breakpoint_reached = False @@ -43,9 +43,14 @@ class Processor: # Maybe implemenet a 'get_pretty_print_state' at module # level? self.ignore_keys = [ - "bit_length", "mask", "increment", "read_from_bus", - "read_from_uADR", "decrement_by_one", "bus_id" - ] + "bit_length", + "mask", + "increment", + "read_from_bus", + "read_from_uADR", + "decrement_by_one", + "bus_id", + ] self.lambdas: dict[str, Callable[..., bool]] = {} # List containing all clock cycles where a new asm instruction started @@ -63,8 +68,8 @@ class Processor: # If a previous stored cycle has been loaded, discard # all stored cycles from that cycle and onward in # the history of saved cycles - self.module_history = self.module_history[0:self.clock] - self.signal_history = self.signal_history[0:self.clock] + self.module_history = self.module_history[0 : self.clock] + self.signal_history = self.signal_history[0 : self.clock] self.unstop() self.save_cycle() @@ -99,7 +104,7 @@ class Processor: """ raise NotImplemented - def run_asm_instruction(self, num_instructions = 1) -> None: + def run_asm_instruction(self, num_instructions=1) -> None: """ Runs assembler instructions on the processors 'num_instructions' times. @@ -109,7 +114,7 @@ class Processor: raise NotImplemented - 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. @@ -117,18 +122,18 @@ class Processor: Undo assembler instrucion assumes the CPU is always at the end of the list 'self.assembly_cycles'. - Undos asm instructions by finding the corresponding clock cycle before + Undos asm instructions by finding the corresponding clock cycle before said number of asm intructions started and loading that clock cycle. """ current_clock_cycle = self.clock index = len(self.assembly_cycles) - saved_cycle = self.assembly_cycles[index-1] + saved_cycle = self.assembly_cycles[index - 1] # Make sure we are undoing the instruction(s) we are currently on while saved_cycle >= current_clock_cycle: index -= 1 - saved_cycle = self.assembly_cycles[index-1] + saved_cycle = self.assembly_cycles[index - 1] index -= num_instructions @@ -140,12 +145,11 @@ class Processor: self.load_cycle(clockcycle) - # Need +1 here since we save the start state to enable to + # Need +1 here since we save the start state to enable to # load the start state. This is done since we only append clock # cycles to the list self.assembly_cycles when we reach a new state # that has uPC set to 0, which wont happend when we load a new file. - self.assembly_cycles = self.assembly_cycles[:index+1] - + self.assembly_cycles = self.assembly_cycles[: index + 1] def run_continuously(self) -> None: """ @@ -156,11 +160,21 @@ class Processor: self.do_tick() def should_halt(self) -> bool: + """ + Return True when the processor should halt, otherwise False. + + Note + ---- + Each processor should implement the condition for True in its subclass. + If we intead raise NotImplemented as we do in all other cases when the + functionality should be implemented in the subclass we lose the ability + to test modules on the base processor class. + """ return False 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. """ self.is_stopped = True @@ -188,7 +202,7 @@ class Processor: """ self.breakpoint_reached = False for _, bp in self.breakpoints.items(): - #TODO: Can make this more efficient by only checking enabled breakpoints + # 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 @@ -196,7 +210,6 @@ class Processor: if self.breakpoint_reached: self.stop() - def reset(self): """ Resets all values in the processor and @@ -216,7 +229,6 @@ class Processor: module = self.update_queue.pop(0) module.update_logic() - def add_modules_to_update(self, module: Module) -> None: """ Queues module to be updated at the end of clock cycle. @@ -254,7 +266,7 @@ class Processor: """ return self.clock - def set_clock(self, value: int) -> int: + def set_clock(self, value: int) -> None: """ Set current clockcycle. """ @@ -294,7 +306,6 @@ class Processor: except IndexError: raise IndexError - for module_name, module_state in module_states.items(): self.modules[module_name].set_state(module_state) @@ -320,12 +331,11 @@ class Processor: file = open(file_path) # Ensure we are at the start of the file - file.seek(0, os.SEEK_END) # Go to end of file - if file.tell(): # If current pos != 0 - file.seek(0) # Return to the start of the file + file.seek(0, os.SEEK_END) # Go to end of file + if file.tell(): # If current pos != 0 + file.seek(0) # Return to the start of the file else: - raise ValueError # Else raise error - + raise ValueError # Else raise error module_name = None module_str = "" @@ -373,7 +383,7 @@ class Processor: def pretty_print(self) -> None: self.pretty_print_verbose(self.ignore_keys) - def pretty_print_verbose(self, ignore_keys = []) -> None: + def pretty_print_verbose(self, ignore_keys=None) -> None: """ Prints the most relevant information about each module compact and pretty. Fields of modules can be ignored with the @@ -383,11 +393,13 @@ class Processor: memory_modules = [] other_modules = [] + if ignore_keys == None: + ignore_keys = [] + # TODO: remove isinstance(module, micro_memory) for module in self.modules.values(): if not isinstance(module, Bus): - if isinstance(module, Memory) \ - or isinstance(module, MicroMemory): + if isinstance(module, Memory) or isinstance(module, MicroMemory): memory_modules.append(module) else: other_modules.append(module) @@ -405,7 +417,7 @@ class Processor: module_to_line_length = {} # get the longest line length for all other_modules for module in other_modules: - line_len = module.get_longest_line_len(ignore_keys) + 3 #+3 for padding + line_len = module.get_longest_line_len(ignore_keys) + 3 # +3 for padding # TODO: what to do if two or more modules has the same name module_to_line_length[module] = line_len @@ -433,16 +445,15 @@ class Processor: # If this is not done, the keys we want to print # could end up on an index that is higher than the # number of rows printed and will therefore be missed - real_keys = [key for key in keys - if key not in ignore_keys] + real_keys = [key for key in keys if key not in ignore_keys] # Dont go out of index # Needed since one module might want to print 4 # fields and another only 1 - if row < len(real_keys) and real_keys[row] \ - not in ignore_keys: - string += real_keys[row] + ": " \ - + str(module_state[real_keys[row]]) + if row < len(real_keys) and real_keys[row] not in ignore_keys: + string += ( + real_keys[row] + ": " + str(module_state[real_keys[row]]) + ) # padd the string so each string has the same # length @@ -462,7 +473,7 @@ class Processor: print(self.line_seperator * self.max_line_len) longest_line_len = module.get_longest_line_len() - #longest_line_len = self.get_longest_memory_value(module.memory) + # longest_line_len = self.get_longest_memory_value(module.memory) string = "" last_mem_len = module.get_largest_mem_adr() @@ -471,11 +482,9 @@ class Processor: # create a new string containg the adress and the value # of the memory adress formattet to fit with the largest # adress and largest memory value - new_row = ((str(i).ljust(last_mem_len) - + ": " - + str(value)).ljust(longest_line_len - + last_mem_len + 3) - + "|") + new_row = (str(i).ljust(last_mem_len) + ": " + str(value)).ljust( + longest_line_len + last_mem_len + 3 + ) + "|" # only add the string if there is space for it, else # print the string and start a new @@ -493,9 +502,7 @@ class Processor: print(self.line_seperator * self.max_line_len) print() - 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 with enough added spacing between the names so that the modules @@ -511,9 +518,9 @@ class Processor: print(name_string) - def group_pp_modules(self, - module_to_line_length: dict[Module, int] - ) -> list[dict[Module, int]]: + def group_pp_modules( + self, module_to_line_length: dict[Module, int] + ) -> list[dict[Module, int]]: """ Groups the modules to be pretty printed into groups with a total line length lower than 'self.max_line_len'. @@ -542,11 +549,13 @@ class Processor: Return a list containing all the keys for a module except the name. Optinal argument to ignore specific keys. """ - return [key for key in module.get_state() if key != "name" - and key not in ignore_keys] + return [ + key + for key in module.get_state() + 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: """ Return the most number of a fields a module has. Can optionally ignore keys. @@ -559,8 +568,9 @@ class Processor: return fields - def add_state_breakpoint(self, module_name: str, state_name: str, - value: Any) -> None: + def add_state_breakpoint( + self, module_name: str, state_name: str, value: Any + ) -> None: if module_name not in self.modules: raise ValueError(f"No module named {module_name}") @@ -568,8 +578,7 @@ class Processor: module = self.modules[module_name] if state_name not in module.get_state(): - raise ValueError(f"No state named {state_name} in " - f"module {module_name}") + raise ValueError(f"No state named {state_name} in " f"module {module_name}") bp = StateBreakpoint(module, state_name, value) self.breakpoints[self.breakpoint_id_counter] = bp @@ -588,7 +597,7 @@ class Processor: for bp_id, bp in self.breakpoints.items(): 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, adress: int, value: Any) -> None: if module_name not in self.modules: raise ValueError(f"No module named {module_name}")