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 23 from devlib.instrument import Instrument, InstrumentChannel, MeasurementsCsv, Measurement, CONTINUOUS, INSTANTANEOUS 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 model=None, 37 modules=None, 38 39 # serial settings 40 serial_port='/dev/ttyS0', 41 baudrate=115200, 42 43 # VExpress MicroSD mount point 44 vemsd_mount=None, 45 46 # supported: dtr, reboottxt 47 hard_reset_method=None, 48 # supported: uefi, uefi-shell, u-boot, bootmon 49 bootloader=None, 50 # supported: vemsd 51 flash_method='vemsd', 52 53 image=None, 54 fdt=None, 55 initrd=None, 56 bootargs=None, 57 58 uefi_entry=None, # only used if bootloader is "uefi" 59 ready_timeout=60, 60 ): 61 super(VersatileExpressPlatform, self).__init__(name, 62 core_names, 63 core_clusters, 64 big_core, 65 model, 66 modules) 67 self.serial_port = serial_port 68 self.baudrate = baudrate 69 self.vemsd_mount = vemsd_mount 70 self.image = image 71 self.fdt = fdt 72 self.initrd = initrd 73 self.bootargs = bootargs 74 self.uefi_entry = uefi_entry 75 self.ready_timeout = ready_timeout 76 self.bootloader = None 77 self.hard_reset_method = None 78 self._set_bootloader(bootloader) 79 self._set_hard_reset_method(hard_reset_method) 80 self._set_flash_method(flash_method) 81 82 def init_target_connection(self, target): 83 if target.os == 'android': 84 self._init_android_target(target) 85 else: 86 self._init_linux_target(target) 87 88 def _init_android_target(self, target): 89 if target.connection_settings.get('device') is None: 90 addr = self._get_target_ip_address(target) 91 target.connection_settings['device'] = addr + ':5555' 92 93 def _init_linux_target(self, target): 94 if target.connection_settings.get('host') is None: 95 addr = self._get_target_ip_address(target) 96 target.connection_settings['host'] = addr 97 98 def _get_target_ip_address(self, target): 99 with open_serial_connection(port=self.serial_port, 100 baudrate=self.baudrate, 101 timeout=30, 102 init_dtr=0) as tty: 103 tty.sendline('') 104 self.logger.debug('Waiting for the Android shell prompt.') 105 tty.expect(target.shell_prompt) 106 107 self.logger.debug('Waiting for IP address...') 108 wait_start_time = time.time() 109 while True: 110 tty.sendline('ip addr list eth0') 111 time.sleep(1) 112 try: 113 tty.expect(r'inet ([1-9]\d*.\d+.\d+.\d+)', timeout=10) 114 return tty.match.group(1) 115 except pexpect.TIMEOUT: 116 pass # We have our own timeout -- see below. 117 if (time.time() - wait_start_time) > self.ready_timeout: 118 raise TargetError('Could not acquire IP address.') 119 120 def _set_hard_reset_method(self, hard_reset_method): 121 if hard_reset_method == 'dtr': 122 self.modules.append({'vexpress-dtr': {'port': self.serial_port, 123 'baudrate': self.baudrate, 124 }}) 125 elif hard_reset_method == 'reboottxt': 126 self.modules.append({'vexpress-reboottxt': {'port': self.serial_port, 127 'baudrate': self.baudrate, 128 'path': self.vemsd_mount, 129 }}) 130 else: 131 ValueError('Invalid hard_reset_method: {}'.format(hard_reset_method)) 132 133 def _set_bootloader(self, bootloader): 134 self.bootloader = bootloader 135 if self.bootloader == 'uefi': 136 self.modules.append({'vexpress-uefi': {'port': self.serial_port, 137 'baudrate': self.baudrate, 138 'image': self.image, 139 'fdt': self.fdt, 140 'initrd': self.initrd, 141 'bootargs': self.bootargs, 142 }}) 143 elif self.bootloader == 'uefi-shell': 144 self.modules.append({'vexpress-uefi-shell': {'port': self.serial_port, 145 'baudrate': self.baudrate, 146 'image': self.image, 147 'bootargs': self.bootargs, 148 }}) 149 elif self.bootloader == 'u-boot': 150 uboot_env = None 151 if self.bootargs: 152 uboot_env = {'bootargs': self.bootargs} 153 self.modules.append({'vexpress-u-boot': {'port': self.serial_port, 154 'baudrate': self.baudrate, 155 'env': uboot_env, 156 }}) 157 elif self.bootloader == 'bootmon': 158 self.modules.append({'vexpress-bootmon': {'port': self.serial_port, 159 'baudrate': self.baudrate, 160 'image': self.image, 161 'fdt': self.fdt, 162 'initrd': self.initrd, 163 'bootargs': self.bootargs, 164 }}) 165 else: 166 ValueError('Invalid hard_reset_method: {}'.format(bootloader)) 167 168 def _set_flash_method(self, flash_method): 169 if flash_method == 'vemsd': 170 self.modules.append({'vexpress-vemsd': {'vemsd_mount': self.vemsd_mount}}) 171 else: 172 ValueError('Invalid flash_method: {}'.format(flash_method)) 173 174 175 class Juno(VersatileExpressPlatform): 176 177 def __init__(self, 178 vemsd_mount='/media/JUNO', 179 baudrate=115200, 180 bootloader='u-boot', 181 hard_reset_method='dtr', 182 **kwargs 183 ): 184 super(Juno, self).__init__('juno', 185 vemsd_mount=vemsd_mount, 186 baudrate=baudrate, 187 bootloader=bootloader, 188 hard_reset_method=hard_reset_method, 189 **kwargs) 190 191 192 class TC2(VersatileExpressPlatform): 193 194 def __init__(self, 195 vemsd_mount='/media/VEMSD', 196 baudrate=38400, 197 bootloader='bootmon', 198 hard_reset_method='reboottxt', 199 **kwargs 200 ): 201 super(TC2, self).__init__('tc2', 202 vemsd_mount=vemsd_mount, 203 baudrate=baudrate, 204 bootloader=bootloader, 205 hard_reset_method=hard_reset_method, 206 **kwargs) 207 208 209 class JunoEnergyInstrument(Instrument): 210 211 binname = 'readenergy' 212 mode = CONTINUOUS | INSTANTANEOUS 213 214 _channels = [ 215 InstrumentChannel('sys', 'current'), 216 InstrumentChannel('a57', 'current'), 217 InstrumentChannel('a53', 'current'), 218 InstrumentChannel('gpu', 'current'), 219 InstrumentChannel('sys', 'voltage'), 220 InstrumentChannel('a57', 'voltage'), 221 InstrumentChannel('a53', 'voltage'), 222 InstrumentChannel('gpu', 'voltage'), 223 InstrumentChannel('sys', 'power'), 224 InstrumentChannel('a57', 'power'), 225 InstrumentChannel('a53', 'power'), 226 InstrumentChannel('gpu', 'power'), 227 InstrumentChannel('sys', 'energy'), 228 InstrumentChannel('a57', 'energy'), 229 InstrumentChannel('a53', 'energy'), 230 InstrumentChannel('gpu', 'energy'), 231 ] 232 233 def __init__(self, target): 234 super(JunoEnergyInstrument, self).__init__(target) 235 self.on_target_file = None 236 self.command = None 237 self.binary = self.target.bin(self.binname) 238 for chan in self._channels: 239 self.channels[chan.name] = chan 240 self.on_target_file = self.target.tempfile('energy', '.csv') 241 self.sample_rate_hz = 10 # DEFAULT_PERIOD is 100[ms] in readenergy.c 242 self.command = '{} -o {}'.format(self.binary, self.on_target_file) 243 self.command2 = '{}'.format(self.binary) 244 245 def setup(self): 246 self.binary = self.target.install(os.path.join(PACKAGE_BIN_DIRECTORY, 247 self.target.abi, self.binname)) 248 249 def reset(self, sites=None, kinds=None): 250 super(JunoEnergyInstrument, self).reset(sites, kinds) 251 self.target.killall(self.binname, as_root=True) 252 253 def start(self): 254 self.target.kick_off(self.command, as_root=True) 255 256 def stop(self): 257 self.target.killall(self.binname, signal='TERM', as_root=True) 258 259 def get_data(self, output_file): 260 temp_file = tempfile.mktemp() 261 self.target.pull(self.on_target_file, temp_file) 262 self.target.remove(self.on_target_file) 263 264 with open(temp_file, 'rb') as fh: 265 reader = csv.reader(fh) 266 headings = reader.next() 267 268 # Figure out which columns from the collected csv we actually want 269 select_columns = [] 270 for chan in self.active_channels: 271 try: 272 select_columns.append(headings.index(chan.name)) 273 except ValueError: 274 raise HostError('Channel "{}" is not in {}'.format(chan.name, temp_file)) 275 276 with open(output_file, 'wb') as wfh: 277 write_headings = ['{}_{}'.format(c.site, c.kind) 278 for c in self.active_channels] 279 writer = csv.writer(wfh) 280 writer.writerow(write_headings) 281 for row in reader: 282 write_row = [row[c] for c in select_columns] 283 writer.writerow(write_row) 284 285 return MeasurementsCsv(output_file, self.active_channels) 286 287 def take_measurement(self): 288 result = [] 289 output = self.target.execute(self.command2).split() 290 reader=csv.reader(output) 291 headings=reader.next() 292 values = reader.next() 293 for chan in self.active_channels: 294 value = values[headings.index(chan.name)] 295 result.append(Measurement(value, chan)) 296 return result 297 298