Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 1 | # Copyright 2015 ARM Limited |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | # |
| 15 | from __future__ import division |
| 16 | import os |
| 17 | import tempfile |
| 18 | import csv |
| 19 | import time |
| 20 | import pexpect |
| 21 | |
| 22 | from devlib.platform import Platform |
Chris Redpath | 0991510 | 2015-12-12 21:46:59 +0000 | [diff] [blame] | 23 | from devlib.instrument import Instrument, InstrumentChannel, MeasurementsCsv, Measurement, CONTINUOUS, INSTANTANEOUS |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 24 | from devlib.exception import TargetError, HostError |
| 25 | from devlib.host import PACKAGE_BIN_DIRECTORY |
| 26 | from devlib.utils.serial_port import open_serial_connection |
| 27 | |
| 28 | |
| 29 | class VersatileExpressPlatform(Platform): |
| 30 | |
| 31 | def __init__(self, name, # pylint: disable=too-many-locals |
| 32 | |
| 33 | core_names=None, |
| 34 | core_clusters=None, |
| 35 | big_core=None, |
| 36 | modules=None, |
| 37 | |
| 38 | # serial settings |
| 39 | serial_port='/dev/ttyS0', |
| 40 | baudrate=115200, |
| 41 | |
| 42 | # VExpress MicroSD mount point |
| 43 | vemsd_mount=None, |
| 44 | |
| 45 | # supported: dtr, reboottxt |
| 46 | hard_reset_method=None, |
| 47 | # supported: uefi, uefi-shell, u-boot, bootmon |
| 48 | bootloader=None, |
| 49 | # supported: vemsd |
| 50 | flash_method='vemsd', |
| 51 | |
| 52 | image=None, |
| 53 | fdt=None, |
| 54 | initrd=None, |
| 55 | bootargs=None, |
| 56 | |
| 57 | uefi_entry=None, # only used if bootloader is "uefi" |
| 58 | ready_timeout=60, |
| 59 | ): |
| 60 | super(VersatileExpressPlatform, self).__init__(name, |
| 61 | core_names, |
| 62 | core_clusters, |
| 63 | big_core, |
| 64 | modules) |
| 65 | self.serial_port = serial_port |
| 66 | self.baudrate = baudrate |
| 67 | self.vemsd_mount = vemsd_mount |
| 68 | self.image = image |
| 69 | self.fdt = fdt |
| 70 | self.initrd = initrd |
| 71 | self.bootargs = bootargs |
| 72 | self.uefi_entry = uefi_entry |
| 73 | self.ready_timeout = ready_timeout |
| 74 | self.bootloader = None |
| 75 | self.hard_reset_method = None |
| 76 | self._set_bootloader(bootloader) |
| 77 | self._set_hard_reset_method(hard_reset_method) |
| 78 | self._set_flash_method(flash_method) |
| 79 | |
| 80 | def init_target_connection(self, target): |
| 81 | if target.os == 'android': |
| 82 | self._init_android_target(target) |
| 83 | else: |
| 84 | self._init_linux_target(target) |
| 85 | |
| 86 | def _init_android_target(self, target): |
| 87 | if target.connection_settings.get('device') is None: |
| 88 | addr = self._get_target_ip_address(target) |
| 89 | target.connection_settings['device'] = addr + ':5555' |
| 90 | |
| 91 | def _init_linux_target(self, target): |
| 92 | if target.connection_settings.get('host') is None: |
| 93 | addr = self._get_target_ip_address(target) |
| 94 | target.connection_settings['host'] = addr |
| 95 | |
| 96 | def _get_target_ip_address(self, target): |
| 97 | with open_serial_connection(port=self.serial_port, |
| 98 | baudrate=self.baudrate, |
| 99 | timeout=30, |
| 100 | init_dtr=0) as tty: |
| 101 | tty.sendline('') |
| 102 | self.logger.debug('Waiting for the Android shell prompt.') |
| 103 | tty.expect(target.shell_prompt) |
| 104 | |
| 105 | self.logger.debug('Waiting for IP address...') |
| 106 | wait_start_time = time.time() |
| 107 | while True: |
| 108 | tty.sendline('ip addr list eth0') |
| 109 | time.sleep(1) |
| 110 | try: |
| 111 | tty.expect(r'inet ([1-9]\d*.\d+.\d+.\d+)', timeout=10) |
| 112 | return tty.match.group(1) |
| 113 | except pexpect.TIMEOUT: |
| 114 | pass # We have our own timeout -- see below. |
| 115 | if (time.time() - wait_start_time) > self.ready_timeout: |
| 116 | raise TargetError('Could not acquire IP address.') |
| 117 | |
| 118 | def _set_hard_reset_method(self, hard_reset_method): |
| 119 | if hard_reset_method == 'dtr': |
| 120 | self.modules.append({'vexpress-dtr': {'port': self.serial_port, |
| 121 | 'baudrate': self.baudrate, |
| 122 | }}) |
| 123 | elif hard_reset_method == 'reboottxt': |
| 124 | self.modules.append({'vexpress-reboottxt': {'port': self.serial_port, |
| 125 | 'baudrate': self.baudrate, |
| 126 | 'path': self.vemsd_mount, |
| 127 | }}) |
| 128 | else: |
| 129 | ValueError('Invalid hard_reset_method: {}'.format(hard_reset_method)) |
| 130 | |
| 131 | def _set_bootloader(self, bootloader): |
| 132 | self.bootloader = bootloader |
| 133 | if self.bootloader == 'uefi': |
| 134 | self.modules.append({'vexpress-uefi': {'port': self.serial_port, |
| 135 | 'baudrate': self.baudrate, |
| 136 | 'image': self.image, |
| 137 | 'fdt': self.fdt, |
| 138 | 'initrd': self.initrd, |
| 139 | 'bootargs': self.bootargs, |
| 140 | }}) |
| 141 | elif self.bootloader == 'uefi-shell': |
| 142 | self.modules.append({'vexpress-uefi-shell': {'port': self.serial_port, |
| 143 | 'baudrate': self.baudrate, |
| 144 | 'image': self.image, |
| 145 | 'bootargs': self.bootargs, |
| 146 | }}) |
| 147 | elif self.bootloader == 'u-boot': |
Sergei Trofimov | a1e991c | 2015-12-15 16:17:08 +0000 | [diff] [blame] | 148 | uboot_env = None |
| 149 | if self.bootargs: |
| 150 | uboot_env = {'bootargs': self.bootargs} |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 151 | self.modules.append({'vexpress-u-boot': {'port': self.serial_port, |
| 152 | 'baudrate': self.baudrate, |
Sergei Trofimov | a1e991c | 2015-12-15 16:17:08 +0000 | [diff] [blame] | 153 | 'env': uboot_env, |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 154 | }}) |
| 155 | elif self.bootloader == 'bootmon': |
| 156 | self.modules.append({'vexpress-bootmon': {'port': self.serial_port, |
| 157 | 'baudrate': self.baudrate, |
| 158 | 'image': self.image, |
| 159 | 'fdt': self.fdt, |
| 160 | 'initrd': self.initrd, |
| 161 | 'bootargs': self.bootargs, |
| 162 | }}) |
| 163 | else: |
| 164 | ValueError('Invalid hard_reset_method: {}'.format(bootloader)) |
| 165 | |
| 166 | def _set_flash_method(self, flash_method): |
| 167 | if flash_method == 'vemsd': |
| 168 | self.modules.append({'vexpress-vemsd': {'vemsd_mount': self.vemsd_mount}}) |
| 169 | else: |
| 170 | ValueError('Invalid flash_method: {}'.format(flash_method)) |
| 171 | |
| 172 | |
| 173 | class Juno(VersatileExpressPlatform): |
| 174 | |
| 175 | def __init__(self, |
| 176 | vemsd_mount='/media/JUNO', |
| 177 | baudrate=115200, |
| 178 | bootloader='u-boot', |
| 179 | hard_reset_method='dtr', |
| 180 | **kwargs |
| 181 | ): |
| 182 | super(Juno, self).__init__('juno', |
| 183 | vemsd_mount=vemsd_mount, |
| 184 | baudrate=baudrate, |
| 185 | bootloader=bootloader, |
| 186 | hard_reset_method=hard_reset_method, |
| 187 | **kwargs) |
| 188 | |
| 189 | |
| 190 | class TC2(VersatileExpressPlatform): |
| 191 | |
| 192 | def __init__(self, |
| 193 | vemsd_mount='/media/VEMSD', |
| 194 | baudrate=38400, |
| 195 | bootloader='bootmon', |
| 196 | hard_reset_method='reboottxt', |
| 197 | **kwargs |
| 198 | ): |
| 199 | super(TC2, self).__init__('tc2', |
| 200 | vemsd_mount=vemsd_mount, |
| 201 | baudrate=baudrate, |
| 202 | bootloader=bootloader, |
| 203 | hard_reset_method=hard_reset_method, |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 204 | **kwargs) |
| 205 | |
| 206 | |
| 207 | class JunoEnergyInstrument(Instrument): |
| 208 | |
| 209 | binname = 'readenergy' |
Chris Redpath | 0991510 | 2015-12-12 21:46:59 +0000 | [diff] [blame] | 210 | mode = CONTINUOUS | INSTANTANEOUS |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 211 | |
| 212 | _channels = [ |
| 213 | InstrumentChannel('sys_curr', 'sys', 'current'), |
| 214 | InstrumentChannel('a57_curr', 'a57', 'current'), |
| 215 | InstrumentChannel('a53_curr', 'a53', 'current'), |
| 216 | InstrumentChannel('gpu_curr', 'gpu', 'current'), |
| 217 | InstrumentChannel('sys_volt', 'sys', 'voltage'), |
| 218 | InstrumentChannel('a57_volt', 'a57', 'voltage'), |
| 219 | InstrumentChannel('a53_volt', 'a53', 'voltage'), |
| 220 | InstrumentChannel('gpu_volt', 'gpu', 'voltage'), |
| 221 | InstrumentChannel('sys_pow', 'sys', 'power'), |
| 222 | InstrumentChannel('a57_pow', 'a57', 'power'), |
| 223 | InstrumentChannel('a53_pow', 'a53', 'power'), |
| 224 | InstrumentChannel('gpu_pow', 'gpu', 'power'), |
| 225 | InstrumentChannel('sys_cenr', 'sys', 'energy'), |
| 226 | InstrumentChannel('a57_cenr', 'a57', 'energy'), |
| 227 | InstrumentChannel('a53_cenr', 'a53', 'energy'), |
| 228 | InstrumentChannel('gpu_cenr', 'gpu', 'energy'), |
| 229 | ] |
| 230 | |
| 231 | def __init__(self, target): |
| 232 | super(JunoEnergyInstrument, self).__init__(target) |
| 233 | self.on_target_file = None |
| 234 | self.command = None |
| 235 | self.binary = self.target.bin(self.binname) |
| 236 | for chan in self._channels: |
| 237 | self.channels[chan.name] = chan |
| 238 | self.on_target_file = self.target.tempfile('energy', '.csv') |
Brendan Jackman | 49b547a | 2017-04-26 15:07:05 +0100 | [diff] [blame] | 239 | self.sample_rate_hz = 10 # DEFAULT_PERIOD is 100[ms] in readenergy.c |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 240 | self.command = '{} -o {}'.format(self.binary, self.on_target_file) |
Chris Redpath | 0991510 | 2015-12-12 21:46:59 +0000 | [diff] [blame] | 241 | self.command2 = '{}'.format(self.binary) |
Sergei Trofimov | 4e6afe9 | 2015-10-09 09:30:04 +0100 | [diff] [blame] | 242 | |
| 243 | def setup(self): |
| 244 | self.binary = self.target.install(os.path.join(PACKAGE_BIN_DIRECTORY, |
| 245 | self.target.abi, self.binname)) |
| 246 | |
| 247 | def reset(self, sites=None, kinds=None): |
| 248 | super(JunoEnergyInstrument, self).reset(sites, kinds) |
| 249 | self.target.killall(self.binname, as_root=True) |
| 250 | |
| 251 | def start(self): |
| 252 | self.target.kick_off(self.command, as_root=True) |
| 253 | |
| 254 | def stop(self): |
| 255 | self.target.killall(self.binname, signal='TERM', as_root=True) |
| 256 | |
| 257 | def get_data(self, output_file): |
| 258 | temp_file = tempfile.mktemp() |
| 259 | self.target.pull(self.on_target_file, temp_file) |
| 260 | self.target.remove(self.on_target_file) |
| 261 | |
| 262 | with open(temp_file, 'rb') as fh: |
| 263 | reader = csv.reader(fh) |
| 264 | headings = reader.next() |
| 265 | |
| 266 | # Figure out which columns from the collected csv we actually want |
| 267 | select_columns = [] |
| 268 | for chan in self.active_channels: |
| 269 | try: |
| 270 | select_columns.append(headings.index(chan.name)) |
| 271 | except ValueError: |
| 272 | raise HostError('Channel "{}" is not in {}'.format(chan.name, temp_file)) |
| 273 | |
| 274 | with open(output_file, 'wb') as wfh: |
| 275 | write_headings = ['{}_{}'.format(c.site, c.kind) |
| 276 | for c in self.active_channels] |
| 277 | writer = csv.writer(wfh) |
| 278 | writer.writerow(write_headings) |
| 279 | for row in reader: |
| 280 | write_row = [row[c] for c in select_columns] |
| 281 | writer.writerow(write_row) |
| 282 | |
| 283 | return MeasurementsCsv(output_file, self.active_channels) |
| 284 | |
Chris Redpath | 0991510 | 2015-12-12 21:46:59 +0000 | [diff] [blame] | 285 | def take_measurement(self): |
| 286 | result = [] |
| 287 | output = self.target.execute(self.command2).split() |
| 288 | reader=csv.reader(output) |
| 289 | headings=reader.next() |
| 290 | values = reader.next() |
| 291 | for chan in self.active_channels: |
| 292 | value = values[headings.index(chan.name)] |
| 293 | result.append(Measurement(value, chan)) |
Chris Redpath | 0991510 | 2015-12-12 21:46:59 +0000 | [diff] [blame] | 294 | return result |
| 295 | |