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# 15from __future__ import division 16import os 17import tempfile 18import csv 19import time 20import pexpect 21 22from devlib.platform import Platform 23from devlib.instrument import Instrument, InstrumentChannel, MeasurementsCsv, Measurement, CONTINUOUS, INSTANTANEOUS 24from devlib.exception import TargetError, HostError 25from devlib.host import PACKAGE_BIN_DIRECTORY 26from devlib.utils.serial_port import open_serial_connection 27 28 29class 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': 148 uboot_env = None 149 if self.bootargs: 150 uboot_env = {'bootargs': self.bootargs} 151 self.modules.append({'vexpress-u-boot': {'port': self.serial_port, 152 'baudrate': self.baudrate, 153 'env': uboot_env, 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 173class 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 190class 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, 204 **kwargs) 205 206 207class JunoEnergyInstrument(Instrument): 208 209 binname = 'readenergy' 210 mode = CONTINUOUS | INSTANTANEOUS 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') 239 self.sample_rate_hz = 10 # DEFAULT_PERIOD is 100[ms] in readenergy.c 240 self.command = '{} -o {}'.format(self.binary, self.on_target_file) 241 self.command2 = '{}'.format(self.binary) 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 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)) 294 return result 295 296