Home | History | Annotate | Download | only in readenergy
      1 /*    Copyright 2014-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 
     16 
     17 /*
     18  * readenergy.c
     19  *
     20  * Reads APB energy registers in Juno and outputs the measurements (converted to appropriate units).
     21  *
     22 */
     23 #include <errno.h>
     24 #include <fcntl.h>
     25 #include <stdint.h>
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <string.h>
     29 #include <signal.h>
     30 #include <sys/mman.h>
     31 #include <sys/stat.h>
     32 #include <sys/types.h>
     33 #include <time.h>
     34 #include <unistd.h>
     35 
     36 // The following values obtained from Juno TRM 2014/03/04 section 4.5
     37 
     38 // Location of APB registers in memory
     39 #define APB_BASE_MEMORY 0x1C010000
     40 // APB energy counters start at offset 0xD0 from the base APB address.
     41 #define BASE_INDEX 0xD0 / 4
     42 // the one-past last APB counter
     43 #define APB_SIZE 0x120
     44 
     45 // Masks specifying the bits that contain the actual counter values
     46 #define CMASK 0xFFF
     47 #define VMASK 0xFFF
     48 #define PMASK 0xFFFFFF
     49 
     50 // Sclaing factor (divisor) or getting measured values from counters
     51 #define SYS_ADC_CH0_PM1_SYS_SCALE 761
     52 #define SYS_ADC_CH1_PM2_A57_SCALE 381
     53 #define SYS_ADC_CH2_PM3_A53_SCALE 761
     54 #define SYS_ADC_CH3_PM4_GPU_SCALE 381
     55 #define SYS_ADC_CH4_VSYS_SCALE 1622
     56 #define SYS_ADC_CH5_VA57_SCALE 1622
     57 #define SYS_ADC_CH6_VA53_SCALE 1622
     58 #define SYS_ADC_CH7_VGPU_SCALE 1622
     59 #define SYS_POW_CH04_SYS_SCALE (SYS_ADC_CH0_PM1_SYS_SCALE * SYS_ADC_CH4_VSYS_SCALE)
     60 #define SYS_POW_CH15_A57_SCALE (SYS_ADC_CH1_PM2_A57_SCALE * SYS_ADC_CH5_VA57_SCALE)
     61 #define SYS_POW_CH26_A53_SCALE (SYS_ADC_CH2_PM3_A53_SCALE * SYS_ADC_CH6_VA53_SCALE)
     62 #define SYS_POW_CH37_GPU_SCALE (SYS_ADC_CH3_PM4_GPU_SCALE * SYS_ADC_CH7_VGPU_SCALE)
     63 #define SYS_ENM_CH0_SYS_SCALE 12348030000
     64 #define SYS_ENM_CH1_A57_SCALE 6174020000
     65 #define SYS_ENM_CH0_A53_SCALE 12348030000
     66 #define SYS_ENM_CH0_GPU_SCALE 6174020000
     67 
     68 // Original values prior to re-callibrations.
     69 /*#define SYS_ADC_CH0_PM1_SYS_SCALE 819.2*/
     70 /*#define SYS_ADC_CH1_PM2_A57_SCALE 409.6*/
     71 /*#define SYS_ADC_CH2_PM3_A53_SCALE 819.2*/
     72 /*#define SYS_ADC_CH3_PM4_GPU_SCALE 409.6*/
     73 /*#define SYS_ADC_CH4_VSYS_SCALE 1638.4*/
     74 /*#define SYS_ADC_CH5_VA57_SCALE 1638.4*/
     75 /*#define SYS_ADC_CH6_VA53_SCALE 1638.4*/
     76 /*#define SYS_ADC_CH7_VGPU_SCALE 1638.4*/
     77 /*#define SYS_POW_CH04_SYS_SCALE (SYS_ADC_CH0_PM1_SYS_SCALE * SYS_ADC_CH4_VSYS_SCALE)*/
     78 /*#define SYS_POW_CH15_A57_SCALE (SYS_ADC_CH1_PM2_A57_SCALE * SYS_ADC_CH5_VA57_SCALE)*/
     79 /*#define SYS_POW_CH26_A53_SCALE (SYS_ADC_CH2_PM3_A53_SCALE * SYS_ADC_CH6_VA53_SCALE)*/
     80 /*#define SYS_POW_CH37_GPU_SCALE (SYS_ADC_CH3_PM4_GPU_SCALE * SYS_ADC_CH7_VGPU_SCALE)*/
     81 /*#define SYS_ENM_CH0_SYS_SCALE 13421772800.0*/
     82 /*#define SYS_ENM_CH1_A57_SCALE 6710886400.0*/
     83 /*#define SYS_ENM_CH0_A53_SCALE 13421772800.0*/
     84 /*#define SYS_ENM_CH0_GPU_SCALE 6710886400.0*/
     85 
     86 // Ignore individual errors but if see too many, abort.
     87 #define ERROR_THRESHOLD 10
     88 
     89 // Default counter poll period (in milliseconds).
     90 #define DEFAULT_PERIOD 100
     91 
     92 // Default duration for the instrument execution (in seconds); 0 means 'forever'
     93 #define DEFAULT_DURATION 0
     94 
     95 // A single reading from the energy meter. The values are the proper readings converted
     96 // to appropriate units (e.g. Watts for power); they are *not* raw counter values.
     97 struct reading
     98 {
     99 	double sys_adc_ch0_pm1_sys;
    100 	double sys_adc_ch1_pm2_a57;
    101 	double sys_adc_ch2_pm3_a53;
    102 	double sys_adc_ch3_pm4_gpu;
    103 	double sys_adc_ch4_vsys;
    104 	double sys_adc_ch5_va57;
    105 	double sys_adc_ch6_va53;
    106 	double sys_adc_ch7_vgpu;
    107 	double sys_pow_ch04_sys;
    108 	double sys_pow_ch15_a57;
    109 	double sys_pow_ch26_a53;
    110 	double sys_pow_ch37_gpu;
    111 	double sys_enm_ch0_sys;
    112 	double sys_enm_ch1_a57;
    113 	double sys_enm_ch0_a53;
    114 	double sys_enm_ch0_gpu;
    115 };
    116 
    117 static inline uint64_t join_64bit_register(uint32_t *buffer, int index)
    118 {
    119 	uint64_t result = 0;
    120 	result |= buffer[index];
    121 	result |= (uint64_t)(buffer[index+1]) << 32;
    122 	return result;
    123 }
    124 
    125 int nsleep(const struct timespec *req, struct timespec *rem)
    126 {
    127 	struct timespec temp_rem;
    128 	if (nanosleep(req, rem) == -1)
    129 	{
    130 		if (errno == EINTR)
    131 		{
    132 			nsleep(rem, &temp_rem);
    133 		}
    134 		else
    135 		{
    136 			return errno;
    137 		}
    138 	}
    139 	else
    140 	{
    141 		return 0;
    142 	}
    143 }
    144 
    145 void print_help()
    146 {
    147 	fprintf(stderr, "Usage: readenergy [-t PERIOD] [-o OUTFILE]\n\n"
    148 			"Read Juno energy counters every PERIOD milliseconds, writing them\n"
    149 			"to OUTFILE in CSV format either until SIGTERM is received OR\n"
    150 			"till the specified duration elapsed.\n"
    151 			"If OUTFILE is not specified, stdout will be used.\n\n"
    152 			"Parameters:\n"
    153 			"	PERIOD is the counter poll period in milliseconds.\n"
    154 			"	       (Defaults to 100 milliseconds.)\n"
    155 			"	DURATION is the duration before execution terminates.\n"
    156 			"		(Defaults to 0 seconds, meaning run till user\n"
    157 			"		terminates execution.\n"
    158 			"	OUTFILE is the output file path\n");
    159 }
    160 
    161 // debugging only...
    162 inline void dprint(char *msg)
    163 {
    164 	fprintf(stderr, "%s\n", msg);
    165 	sync();
    166 }
    167 
    168 // -------------------------------------- config ----------------------------------------------------
    169 
    170 struct config
    171 {
    172 	struct timespec period;
    173 	char *output_file;
    174 	long duration_in_sec;
    175 };
    176 
    177 void config_init_period_from_millis(struct config *this, long millis)
    178 {
    179 	this->period.tv_sec = (time_t)(millis / 1000);
    180 	this->period.tv_nsec = (millis % 1000) * 1000000;
    181 }
    182 
    183 void config_init(struct config *this, int argc, char *argv[])
    184 {
    185 	this->output_file = NULL;
    186 	config_init_period_from_millis(this, DEFAULT_PERIOD);
    187 	this->duration_in_sec = DEFAULT_DURATION;
    188 
    189 	int opt;
    190 	while ((opt = getopt(argc, argv, "ht:o:d:")) != -1)
    191 	{
    192 		switch(opt)
    193 		{
    194 			case 't':
    195 				config_init_period_from_millis(this, atol(optarg));
    196 				break;
    197 			case 'o':
    198 				this->output_file = optarg;
    199 				break;
    200 			case 'd':
    201 				this->duration_in_sec = atol(optarg);
    202 				break;
    203 			case 'h':
    204 				print_help();
    205 				exit(EXIT_SUCCESS);
    206 				break;
    207 			default:
    208 				fprintf(stderr, "ERROR: Unexpected option %s\n\n", opt);
    209 				print_help();
    210 				exit(EXIT_FAILURE);
    211 		}
    212 	}
    213 }
    214 
    215 // -------------------------------------- /config ---------------------------------------------------
    216 
    217 // -------------------------------------- emeter ----------------------------------------------------
    218 
    219 struct emeter
    220 {
    221 	int fd;
    222 	FILE *out;
    223 	void *mmap_base;
    224 };
    225 
    226 void emeter_init(struct emeter *this, char *outfile)
    227 {
    228 	if(outfile)
    229 	{
    230 		this->out = fopen(outfile, "w");
    231 		if (this->out == NULL)
    232 		{
    233 			fprintf(stderr, "ERROR: Could not open output file %s; got %s\n", outfile, strerror(errno));
    234 			exit(EXIT_FAILURE);
    235 		}
    236 	} else {
    237 		this->out = stdout;
    238 	}
    239         this->fd = open("/dev/mem", O_RDONLY);
    240         if(this->fd < 0)
    241         {
    242                 fprintf(stderr, "ERROR: Can't open /dev/mem; got %s\n", strerror(errno));
    243 		fclose(this->out);
    244 		exit(EXIT_FAILURE);
    245         }
    246 
    247 	this->mmap_base = mmap(NULL, APB_SIZE, PROT_READ, MAP_SHARED, this->fd, APB_BASE_MEMORY);
    248 	if (this->mmap_base == MAP_FAILED)
    249 	{
    250 		fprintf(stderr, "ERROR: mmap failed; got %s\n", strerror(errno));
    251 		close(this->fd);
    252 		fclose(this->out);
    253 		exit(EXIT_FAILURE);
    254 	}
    255 
    256 	if(this->out) {
    257 		fprintf(this->out, "sys_curr,a57_curr,a53_curr,gpu_curr,"
    258 				   "sys_volt,a57_volt,a53_volt,gpu_volt,"
    259 				   "sys_pow,a57_pow,a53_pow,gpu_pow,"
    260 				   "sys_cenr,a57_cenr,a53_cenr,gpu_cenr\n");
    261 	}
    262 }
    263 
    264 void emeter_read_measurements(struct emeter *this, struct reading *reading)
    265 {
    266 	uint32_t *buffer = (uint32_t *)this->mmap_base;
    267 	reading->sys_adc_ch0_pm1_sys = (double)(CMASK & buffer[BASE_INDEX+0]) / SYS_ADC_CH0_PM1_SYS_SCALE;
    268 	reading->sys_adc_ch1_pm2_a57 = (double)(CMASK & buffer[BASE_INDEX+1]) / SYS_ADC_CH1_PM2_A57_SCALE;
    269 	reading->sys_adc_ch2_pm3_a53 = (double)(CMASK & buffer[BASE_INDEX+2]) / SYS_ADC_CH2_PM3_A53_SCALE;
    270 	reading->sys_adc_ch3_pm4_gpu = (double)(CMASK & buffer[BASE_INDEX+3]) / SYS_ADC_CH3_PM4_GPU_SCALE;
    271 	reading->sys_adc_ch4_vsys = (double)(VMASK & buffer[BASE_INDEX+4]) / SYS_ADC_CH4_VSYS_SCALE;
    272 	reading->sys_adc_ch5_va57 = (double)(VMASK & buffer[BASE_INDEX+5]) / SYS_ADC_CH5_VA57_SCALE;
    273 	reading->sys_adc_ch6_va53 = (double)(VMASK & buffer[BASE_INDEX+6]) / SYS_ADC_CH6_VA53_SCALE;
    274 	reading->sys_adc_ch7_vgpu = (double)(VMASK & buffer[BASE_INDEX+7]) / SYS_ADC_CH7_VGPU_SCALE;
    275 	reading->sys_pow_ch04_sys = (double)(PMASK & buffer[BASE_INDEX+8]) / SYS_POW_CH04_SYS_SCALE;
    276 	reading->sys_pow_ch15_a57 = (double)(PMASK & buffer[BASE_INDEX+9]) / SYS_POW_CH15_A57_SCALE;
    277 	reading->sys_pow_ch26_a53 = (double)(PMASK & buffer[BASE_INDEX+10]) / SYS_POW_CH26_A53_SCALE;
    278 	reading->sys_pow_ch37_gpu = (double)(PMASK & buffer[BASE_INDEX+11]) / SYS_POW_CH37_GPU_SCALE;
    279 	reading->sys_enm_ch0_sys = (double)join_64bit_register(buffer, BASE_INDEX+12) / SYS_ENM_CH0_SYS_SCALE;
    280 	reading->sys_enm_ch1_a57 = (double)join_64bit_register(buffer, BASE_INDEX+14) / SYS_ENM_CH1_A57_SCALE;
    281 	reading->sys_enm_ch0_a53 = (double)join_64bit_register(buffer, BASE_INDEX+16) / SYS_ENM_CH0_A53_SCALE;
    282 	reading->sys_enm_ch0_gpu = (double)join_64bit_register(buffer, BASE_INDEX+18) / SYS_ENM_CH0_GPU_SCALE;
    283 }
    284 
    285 void emeter_take_reading(struct emeter *this)
    286 {
    287 	static struct reading reading;
    288 	int error_count = 0;
    289 	emeter_read_measurements(this, &reading);
    290 	int ret = fprintf(this->out, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n",
    291 			reading.sys_adc_ch0_pm1_sys,
    292 			reading.sys_adc_ch1_pm2_a57,
    293 			reading.sys_adc_ch2_pm3_a53,
    294 			reading.sys_adc_ch3_pm4_gpu,
    295 			reading.sys_adc_ch4_vsys,
    296 			reading.sys_adc_ch5_va57,
    297 			reading.sys_adc_ch6_va53,
    298 			reading.sys_adc_ch7_vgpu,
    299 			reading.sys_pow_ch04_sys,
    300 			reading.sys_pow_ch15_a57,
    301 			reading.sys_pow_ch26_a53,
    302 			reading.sys_pow_ch37_gpu,
    303 			reading.sys_enm_ch0_sys,
    304 			reading.sys_enm_ch1_a57,
    305 			reading.sys_enm_ch0_a53,
    306 			reading.sys_enm_ch0_gpu);
    307 	if (ret < 0)
    308 	{
    309 		fprintf(stderr, "ERROR: while writing a meter reading: %s\n", strerror(errno));
    310 		if (++error_count > ERROR_THRESHOLD)
    311 			exit(EXIT_FAILURE);
    312 	}
    313 }
    314 
    315 void emeter_finalize(struct emeter *this)
    316 {
    317 	if (munmap(this->mmap_base, APB_SIZE) == -1)
    318 	{
    319 		// Report the error but don't bother doing anything else, as we're not gonna do
    320 		// anything with emeter after this point anyway.
    321 		fprintf(stderr, "ERROR: munmap failed; got %s\n", strerror(errno));
    322 	}
    323 	close(this->fd);
    324 	fclose(this->out);
    325 }
    326 
    327 // -------------------------------------- /emeter ----------------------------------------------------
    328 
    329 volatile int done = 0;
    330 
    331 void term_handler(int signum)
    332 {
    333 	done = 1;
    334 }
    335 
    336 void sigalrm_handler(int signum)
    337 {
    338 	done = 1;
    339 }
    340 
    341 
    342 int main(int argc, char *argv[])
    343 {
    344 	struct sigaction action;
    345 	memset(&action, 0, sizeof(struct sigaction));
    346 	action.sa_handler = term_handler;
    347 	sigaction(SIGTERM, &action, NULL);
    348 
    349 	struct config config;
    350 	struct emeter emeter;
    351 	config_init(&config, argc, argv);
    352 	emeter_init(&emeter, config.output_file);
    353 
    354 	if (0 != config.duration_in_sec)
    355 	{
    356 		/*Set the alarm with the duration from use only if a non-zero value is specified
    357 		  else it will run forever until SIGTERM signal received from user*/
    358 		/*Set the signal handler first*/
    359 		signal(SIGALRM, sigalrm_handler);
    360 		/*Now set the alarm for the duration specified by the user*/
    361 		alarm(config.duration_in_sec);
    362 
    363 	}
    364 
    365 	if(config.output_file)
    366 	{
    367 		struct timespec remaining;
    368 		while (!done)
    369 		{
    370 			emeter_take_reading(&emeter);
    371 			nsleep(&config.period, &remaining);
    372 		}
    373 	} else 	{
    374 		emeter_take_reading(&emeter);
    375 	}
    376 
    377 	emeter_finalize(&emeter);
    378 	return EXIT_SUCCESS;
    379 }
    380