diff options
Diffstat (limited to 'lib/codegen.py')
-rw-r--r-- | lib/codegen.py | 82 |
1 files changed, 72 insertions, 10 deletions
diff --git a/lib/codegen.py b/lib/codegen.py index 98eeafe..75c5102 100644 --- a/lib/codegen.py +++ b/lib/codegen.py @@ -98,6 +98,7 @@ class SimulatedAccountingMethod: * timer counter size (e.g. a 16-bit timer at 1MHz will overflow after 65us) * variable size for accounting of durations, power and energy values """ + def __init__(self, pta: PTA, timer_freq_hz, timer_type, ts_type, power_type, energy_type, ts_granularity = 1e-6, power_granularity = 1e-6, energy_granularity = 1e-12): """ Simulate Online Accounting for a given PTA. @@ -121,50 +122,101 @@ class SimulatedAccountingMethod: self.power_granularity = power_granularity self.energy_granularity = energy_granularity + """Energy in pJ.""" self.energy = self.energy_class(0) + def _energy_from_power_and_time(self, power, time): + """ + Return energy (=power * time), accounting for configured granularity. + + Does not use Module types and therefore does not consider overflows or data-type limitations""" + if self.energy_granularity == self.power_granularity * self.ts_granularity: + return power * time + return int(power * self.power_granularity * time * self.ts_granularity / self.energy_granularity) + def _sleep_duration(self, duration_us): u""" - Return the sleep duration a timer with the configured timer frequency would measure, in µs + Return the sleep duration a timer with the configured timer frequency would measure, according to the configured granularity. - I.e., for a 35us sleep with a 50kHz timer (-> one tick per 20us), the OS would likely measure one tick == 20us. + I.e., for a 35us sleep with a 50kHz timer (-> one tick per 20us) and 1us time resolution, the OS would likely measure one tick == 20us. This is based on the assumption that the timer is reset at each transition, so the duration of states may be under-, but not over-estimated """ us_per_tick = 1000000 / self.timer_freq_hz ticks = self.timer_class(int(duration_us // us_per_tick)) - return int(ticks.val * us_per_tick) + time_units_per_tick = 1 / (self.timer_freq_hz * self.ts_granularity) + return int(ticks.val * time_units_per_tick) def sleep(self, duration_us): pass def pass_transition(self, transition: Transition): + """Updates current state to `transition.destination`.""" self.current_state = transition.destination def get_energy(self): - """ - Return total energy in pJ - """ + """Return total energy in pJ.""" return self.energy.val * self.energy_granularity * 1e12 class SimulatedStaticStateOnlyAccountingImmediateCalculation(SimulatedAccountingMethod): + """ + Simulated state-only energy accounting with immediate calculation. + + Does not use functions or LUTs, only static (median) state power. + Transitions are assumed to be immediate and have negligible energy overhead. + + Keeps track of the current state and the time it is active. On each + transition, current state power and duration is used to update the + total energy spent. + """ + def __init__(self, pta: PTA, *args, **kwargs): super().__init__(pta, *args, **kwargs) def sleep(self, duration_us): - self.energy += self.ts_class(self._sleep_duration(duration_us)) * self.power_class(int(self.current_state.power)) + time = self._sleep_duration(duration_us) + power = int(self.current_state.power) + energy = self._energy_from_power_and_time(time, power) + self.energy += energy class SimulatedStaticAccountingImmediateCalculation(SimulatedAccountingMethod): + """ + Simulated energy accounting with states and transitions, immediate calculation. + + Does not use functions or LUTs, only static (median) state power and transition energ. + + Keeps track of the current state and the time it is active. On each + transition, current state power and duration is used to calculate the + energy spent in the state, which is used in conjunction with the + transition's energy cost to update the total energy spent. + """ + def __init__(self, pta: PTA, *args, **kwargs): super().__init__(pta, *args, **kwargs) def sleep(self, duration_us): - self.energy += self.ts_class(self._sleep_duration(duration_us)) * self.power_class(int(self.current_state.power)) + time = self._sleep_duration(duration_us) + print('sleep duration is {}'.format(time)) + power = int(self.current_state.power) + print('power is {}'.format(power)) + energy = self._energy_from_power_and_time(time, power) + print('energy is {}'.format(energy)) + self.energy += energy def pass_transition(self, transition: Transition): self.energy += int(transition.energy) super().pass_transition(transition) class SimulatedStaticAccounting(SimulatedAccountingMethod): + """ + Simulated energy accounting with states and transitions, deferred energy calculation. + + Does not use functions or LUTs, only static (median) state power and transition energ. + + Keeps track of the time spent in each state and the number of calls for + each transition. This data is update whenever passing a transition and used + to calculate total energy spent on-demand: E = sum(P_q * t_q) + sum(E_t * n_t). + """ + def __init__(self, pta: PTA, *args, **kwargs): super().__init__(pta, *args, **kwargs) self.time_in_state = dict() @@ -185,13 +237,23 @@ class SimulatedStaticAccounting(SimulatedAccountingMethod): pta = self.pta energy = self.energy_class(0) for state in pta.state.values(): - energy += self.time_in_state[state.name] * int(state.power) + energy += self._energy_from_power_and_time(self.time_in_state[state.name], int(state.power)) for i, transition in enumerate(pta.transitions): energy += self.transition_count[i] * int(transition.energy) return energy.val class SimulatedStaticStateOnlyAccounting(SimulatedAccountingMethod): + """ + Simulated energy accounting with states and transitions, deferred energy calculation. + + Does not use functions or LUTs, only static (median) state power and transition energ. + + Keeps track of the time spent in each state and the number of calls for + each transition. This data is update whenever passing a transition and used + to calculate total energy spent on-demand: E = sum(P_q * t_q) + sum(E_t * n_t). + """ + def __init__(self, pta: PTA, *args, **kwargs): super().__init__(pta, *args, **kwargs) self.time_in_state = dict() @@ -205,7 +267,7 @@ class SimulatedStaticStateOnlyAccounting(SimulatedAccountingMethod): pta = self.pta energy = self.energy_class(0) for state in pta.state.values(): - energy += self.time_in_state[state.name] * int(state.power) + energy += self._energy_from_power_and_time(self.time_in_state[state.name], int(state.power)) return energy.val class AccountingMethod: |