diff --git a/wa/instruments/delay.py b/wa/instruments/delay.py new file mode 100644 index 00000000..1f260e25 --- /dev/null +++ b/wa/instruments/delay.py @@ -0,0 +1,229 @@ +# 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): + import ipdb; ipdb.set_trace() + 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