# Copyright 2013-2018 ARM Limited # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # pylint: disable=W0613,E1101,E0203,W0201 import time from wa import Instrument, Parameter from wa.framework.exception import ConfigError, InstrumentError from wa.framework.instrument import extremely_slow class DelayInstrument(Instrument): name = 'delay' description = """ This instrument introduces a delay before beginning a new spec, a new job or before the main execution of a workload. The delay may be specified as either a fixed period or a temperature threshold that must be reached. Optionally, if an active cooling solution is available on the device tqgitq speed up temperature drop between runs, it may be controlled using this instrument. """ parameters = [ Parameter('temperature_file', default='/sys/devices/virtual/thermal/thermal_zone0/temp', global_alias='thermal_temp_file', description=""" Full path to the sysfile on the target that contains the target's temperature. """), Parameter('temperature_timeout', kind=int, default=600, global_alias='thermal_timeout', description=""" The timeout after which the instrument will stop waiting even if the specified threshold temperature is not reached. If this timeout is hit, then a warning will be logged stating the actual temperature at which the timeout has ended. """), Parameter('temperature_poll_period', kind=int, default=5, global_alias='thermal_sleep_time', description=""" How long to sleep (in seconds) between polling current target temperature. """), Parameter('temperature_between_specs', kind=int, default=None, global_alias='thermal_threshold_between_specs', description=""" Temperature (in target-specific units) the target must cool down to before the iteration spec will be run. If this is set to ``0`` then the devices initial temperature will used as the threshold. .. note:: This cannot be specified at the same time as ``fixed_between_specs`` """), Parameter('fixed_between_specs', kind=int, default=None, global_alias='fixed_delay_between_specs', description=""" How long to sleep (in seconds) before starting a new workload spec. .. note:: This cannot be specified at the same time as ``temperature_between_specs`` """), Parameter('temperature_between_jobs', kind=int, default=None, global_alias='thermal_threshold_between_jobs', aliases=['temperature_between_iterations'], description=""" Temperature (in target-specific units) the target must cool down to before the next job will be run. If this is set to ``0`` then the devices initial temperature will used as the threshold. .. note:: This cannot be specified at the same time as ``fixed_between_jobs`` """), Parameter('fixed_between_jobs', kind=int, default=None, global_alias='fixed_delay_between_jobs', aliases=['fixed_between_iterations'], description=""" How long to sleep (in seconds) before starting each new job. .. note:: This cannot be specified at the same time as ``temperature_between_jobs`` """), Parameter('fixed_before_start', kind=int, default=None, global_alias='fixed_delay_before_start', description=""" How long to sleep (in seconds) after setup for an iteration has been performed but before running the workload. .. note:: This cannot be specified at the same time as ``temperature_before_start`` """), Parameter('temperature_before_start', kind=int, default=None, global_alias='thermal_threshold_before_start', description=""" Temperature (in device-specific units) the device must cool down to just before the actual workload execution (after setup has been performed). .. note:: This cannot be specified at the same time as ``fixed_between_jobs`` """), Parameter('active_cooling', kind=bool, default=False, description=""" This instrument supports an active cooling solution while waiting for the device temperature to drop to the threshold. If you wish to use this feature please ensure the relevant module is installed on the device. """), ] active_cooling_modules = ['mbed-fan', 'odroidxu3-fan'] def initialize(self, context): if self.active_cooling: self.cooling = self._discover_cooling_module() if not self.cooling: msg = 'Cooling module not found on target. Please install one of the following modules: {}' raise InstrumentError(msg.format(self.active_cooling_modules)) if self.temperature_between_jobs == 0: temp = self.target.read_int(self.temperature_file) self.logger.debug('Setting temperature threshold between jobs to {}'.format(temp)) self.temperature_between_jobs = temp if self.temperature_between_specs == 0: temp = self.target.read_int(self.temperature_file) msg = 'Setting temperature threshold between workload specs to {}' self.logger.debug(msg.format(temp)) self.temperature_between_specs = temp @extremely_slow def start(self, context): if self.fixed_before_start: msg = 'Waiting for {}s before running workload...' self.logger.info(msg.format(self.fixed_before_start)) time.sleep(self.fixed_before_start) elif self.temperature_before_start: self.logger.info('Waiting for temperature drop before running workload...') self.wait_for_temperature(self.temperature_before_start) @extremely_slow def before_job(self, context): if self.fixed_between_specs and context.spec_changed: msg = 'Waiting for {}s before starting new spec...' self.logger.info(msg.format(self.fixed_between_specs)) time.sleep(self.fixed_between_specs) elif self.temperature_between_jobs and context.spec_changed: self.logger.info('Waiting for temperature drop before starting new spec...') self.wait_for_temperature(self.temperature_between_jobs) elif self.fixed_between_jobs: msg = 'Waiting for {}s before starting new job...' self.logger.info(msg.format(self.fixed_between_jobs)) time.sleep(self.fixed_between_jobs) elif self.temperature_between_jobs: self.logger.info('Waiting for temperature drop before starting new job...') self.wait_for_temperature(self.temperature_between_jobs) def wait_for_temperature(self, temperature): if self.active_cooling: self.cooling.start() self.do_wait_for_temperature(temperature) self.cooling.stop() else: self.do_wait_for_temperature(temperature) def do_wait_for_temperature(self, temperature): reading = self.target.read_int(self.temperature_file) waiting_start_time = time.time() while reading > temperature: self.logger.debug('target temperature: {}'.format(reading)) if time.time() - waiting_start_time > self.temperature_timeout: self.logger.warning('Reached timeout; current temperature: {}'.format(reading)) break time.sleep(self.temperature_poll_period) reading = self.target.read_int(self.temperature_file) def validate(self): if (self.temperature_between_specs is not None and self.fixed_between_specs is not None): raise ConfigError('Both fixed delay and thermal threshold specified for specs.') if (self.temperature_between_jobs is not None and self.fixed_between_jobs is not None): raise ConfigError('Both fixed delay and thermal threshold specified for jobs.') if (self.temperature_before_start is not None and self.fixed_before_start is not None): raise ConfigError('Both fixed delay and thermal threshold specified before start.') if not any([self.temperature_between_specs, self.fixed_between_specs, self.temperature_between_jobs, self.fixed_between_jobs, self.temperature_before_start, self.fixed_before_start]): raise ConfigError('Delay instrument is enabled, but no delay is specified.') def _discover_cooling_module(self): cooling_module = None for module in self.active_cooling_modules: if self.target.has(module): if not cooling_module: cooling_module = getattr(self.target, module) else: msg = 'Multiple cooling modules found "{}" "{}".' raise InstrumentError(msg.format(cooling_module.name, module)) return cooling_module