mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-01-18 20:11:20 +00:00
aa4df95a69
PEP8 has switched its guidance [1] for where a line break should occur in relation to a binary operator, so don't raise this warning for new code and update the code base to follow the new style. [1] https://www.python.org/dev/peps/pep-0008/#should-a-line-break-before-or-after-a-binary-operator
231 lines
10 KiB
Python
231 lines
10 KiB
Python
# 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
|
|
from wa.utils.types import identifier
|
|
|
|
|
|
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 to
|
|
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, identifier(module))
|
|
else:
|
|
msg = 'Multiple cooling modules found "{}" "{}".'
|
|
raise InstrumentError(msg.format(cooling_module.name, module))
|
|
return cooling_module
|