2026-07-10 10:38:57 +02:00

114 lines
3.8 KiB
Python

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)