from ScEpTIC.emulator.energy.mcu.options import MCUClockCycleAction from . import CustomInstruction class SimulateClockCycles(CustomInstruction): """ SimulateClockCycles() simulates the execution of a given number of clock cycles params for the equivalent function call: - number of clock cycles to simulate - ratio of register operations - ratio of volatile memory accesses - ratio of non-volatile memory accesses """ entities = [] @classmethod def reset(cls): for entity in cls.entities: entity.executed_instructions = 0 def __init__(self, call_operation): super().__init__(call_operation) self.n_cycles = self.get_arg(0) self.ratios = [float(self.get_arg(1)), float(self.get_arg(2)), float(self.get_arg(3))] if sum(self.ratios) != 1.0: raise Exception("Operation ratios do not sum up to 1") self.t_base, self.schedule = self.__generate_schedule(self.ratios) self.executed_instructions = 0 self.current_instruction = None self.entities.append(self) def get_instruction_type(self): """ Returns the instruction to execute """ return self.current_instruction def get_simulation_data(self): return {'schedule': self.schedule, 'cycles': self.n_cycles, 'base_period': self.t_base} def __generate_schedule(self, ratios): # Possible periods length: possible base periods that lead to integer deadlines t_bases = [10, 20, 50, 100, 200, 500, 1000] ops = [MCUClockCycleAction.NO_MEMORY_ACCESS, MCUClockCycleAction.VOLATILE_MEMORY_ACCESS, MCUClockCycleAction.NON_VOLATILE_MEMORY_ACCESS] t_base = 0 periods = [] int_ratios = [] for t in t_bases: # Create integer ratios to identify the minimum possible base period int_ratios = [int(x * t) for x in ratios if x * t == int(x * t)] # Check if all operations have an integer ratio if len(int_ratios) == len(ratios): t_base = t # Generate periods, accounting for operations never executed (i.e. ratio = 0) periods = [t / x if x != 0 else t_base+1 for x in int_ratios] break # If t_base not set -> an operation has a non-integer period -> not supported (max 2 numbers after comma) if t_base == 0: raise Exception("Operation ratios not supported in a period of up to 100ops!") schedule = [] # Initial deadlines deadlines = [x for x in periods] # Generate schedule for _ in range(0, t_base): # Element with the lowest deadline op = min(range(len(deadlines)), key=deadlines.__getitem__) schedule.append(ops[op]) # Update deadline deadlines[op] = deadlines[op] + periods[op] n = {} error = False # Test schedule for i in range(len(ops)): # Count occurrences of operation ops[i] n[i] = len([x for x in schedule if x == ops[i]]) if n[i] != int_ratios[i]: error = True if error: raise Exception(f"Wrong schedule: {ops}\nRequested: {int_ratios}\nActual: {n}\n") return t_base, schedule def run(self, update_program_counter=True): # Set current instruction self.current_instruction = self.schedule[self.executed_instructions % self.t_base] # Increment executed instructions self.executed_instructions += 1 # Update program counter if end reached end_reached = self.executed_instructions >= self.n_cycles # Reset if end reached if end_reached: self.executed_instructions = 0 super().run(update_program_counter=end_reached)