mirror of
				https://github.com/ARM-software/workload-automation.git
				synced 2025-10-30 22:54:18 +00:00 
			
		
		
		
	Initial commit of open source Workload Automation.
This commit is contained in:
		
							
								
								
									
										11
									
								
								wlauto/external/readenergy/Makefile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								wlauto/external/readenergy/Makefile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| # To build: | ||||
| # | ||||
| # CROSS_COMPILE=aarch64-linux-gnu- make | ||||
| # | ||||
| CROSS_COMPILE?=aarch64-linux-gnu- | ||||
| CC=$(CROSS_COMPILE)gcc | ||||
| CFLAGS='-Wl,-static -Wl,-lc' | ||||
|  | ||||
| readenergy: readenergy.c | ||||
| 	$(CC) $(CFLAGS) readenergy.c -o readenergy | ||||
| 	cp readenergy ../../instrumentation/juno_energy/readenergy | ||||
							
								
								
									
										
											BIN
										
									
								
								wlauto/external/readenergy/readenergy
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								wlauto/external/readenergy/readenergy
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										345
									
								
								wlauto/external/readenergy/readenergy.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								wlauto/external/readenergy/readenergy.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,345 @@ | ||||
| /*    Copyright 2014-2015 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. | ||||
| */ | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * readenergy.c  | ||||
|  * | ||||
|  * Reads APB energy registers in Juno and outputs the measurements (converted to appropriate units). | ||||
|  * | ||||
| */ | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <signal.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
| #include <time.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| // The following values obtained from Juno TRM 2014/03/04 section 4.5 | ||||
|  | ||||
| // Location of APB registers in memory | ||||
| #define APB_BASE_MEMORY 0x1C010000 | ||||
| // APB energy counters start at offset 0xD0 from the base APB address. | ||||
| #define BASE_INDEX 0xD0 / 4 | ||||
| // the one-past last APB counter | ||||
| #define APB_SIZE 0x120 | ||||
|  | ||||
| // Masks specifying the bits that contain the actual counter values | ||||
| #define CMASK 0xFFF | ||||
| #define VMASK 0xFFF | ||||
| #define PMASK 0xFFFFFF | ||||
|  | ||||
| // Sclaing factor (divisor) or getting measured values from counters | ||||
| #define SYS_ADC_CH0_PM1_SYS_SCALE 761 | ||||
| #define SYS_ADC_CH1_PM2_A57_SCALE 381 | ||||
| #define SYS_ADC_CH2_PM3_A53_SCALE 761 | ||||
| #define SYS_ADC_CH3_PM4_GPU_SCALE 381 | ||||
| #define SYS_ADC_CH4_VSYS_SCALE 1622 | ||||
| #define SYS_ADC_CH5_VA57_SCALE 1622 | ||||
| #define SYS_ADC_CH6_VA53_SCALE 1622 | ||||
| #define SYS_ADC_CH7_VGPU_SCALE 1622 | ||||
| #define SYS_POW_CH04_SYS_SCALE (SYS_ADC_CH0_PM1_SYS_SCALE * SYS_ADC_CH4_VSYS_SCALE) | ||||
| #define SYS_POW_CH15_A57_SCALE (SYS_ADC_CH1_PM2_A57_SCALE * SYS_ADC_CH5_VA57_SCALE) | ||||
| #define SYS_POW_CH26_A53_SCALE (SYS_ADC_CH2_PM3_A53_SCALE * SYS_ADC_CH6_VA53_SCALE) | ||||
| #define SYS_POW_CH37_GPU_SCALE (SYS_ADC_CH3_PM4_GPU_SCALE * SYS_ADC_CH7_VGPU_SCALE) | ||||
| #define SYS_ENM_CH0_SYS_SCALE 12348030000 | ||||
| #define SYS_ENM_CH1_A57_SCALE 6174020000 | ||||
| #define SYS_ENM_CH0_A53_SCALE 12348030000 | ||||
| #define SYS_ENM_CH0_GPU_SCALE 6174020000 | ||||
|  | ||||
| // Original values prior to re-callibrations. | ||||
| /*#define SYS_ADC_CH0_PM1_SYS_SCALE 819.2*/ | ||||
| /*#define SYS_ADC_CH1_PM2_A57_SCALE 409.6*/ | ||||
| /*#define SYS_ADC_CH2_PM3_A53_SCALE 819.2*/ | ||||
| /*#define SYS_ADC_CH3_PM4_GPU_SCALE 409.6*/ | ||||
| /*#define SYS_ADC_CH4_VSYS_SCALE 1638.4*/ | ||||
| /*#define SYS_ADC_CH5_VA57_SCALE 1638.4*/ | ||||
| /*#define SYS_ADC_CH6_VA53_SCALE 1638.4*/ | ||||
| /*#define SYS_ADC_CH7_VGPU_SCALE 1638.4*/ | ||||
| /*#define SYS_POW_CH04_SYS_SCALE (SYS_ADC_CH0_PM1_SYS_SCALE * SYS_ADC_CH4_VSYS_SCALE)*/ | ||||
| /*#define SYS_POW_CH15_A57_SCALE (SYS_ADC_CH1_PM2_A57_SCALE * SYS_ADC_CH5_VA57_SCALE)*/ | ||||
| /*#define SYS_POW_CH26_A53_SCALE (SYS_ADC_CH2_PM3_A53_SCALE * SYS_ADC_CH6_VA53_SCALE)*/ | ||||
| /*#define SYS_POW_CH37_GPU_SCALE (SYS_ADC_CH3_PM4_GPU_SCALE * SYS_ADC_CH7_VGPU_SCALE)*/ | ||||
| /*#define SYS_ENM_CH0_SYS_SCALE 13421772800.0*/ | ||||
| /*#define SYS_ENM_CH1_A57_SCALE 6710886400.0*/ | ||||
| /*#define SYS_ENM_CH0_A53_SCALE 13421772800.0*/ | ||||
| /*#define SYS_ENM_CH0_GPU_SCALE 6710886400.0*/ | ||||
|  | ||||
| // Ignore individual errors but if see too many, abort. | ||||
| #define ERROR_THRESHOLD 10 | ||||
|  | ||||
| // Default counter poll period (in milliseconds). | ||||
| #define DEFAULT_PERIOD 100 | ||||
|  | ||||
| // A single reading from the energy meter. The values are the proper readings converted | ||||
| // to appropriate units (e.g. Watts for power); they are *not* raw counter values. | ||||
| struct reading | ||||
| { | ||||
| 	double sys_adc_ch0_pm1_sys; | ||||
| 	double sys_adc_ch1_pm2_a57; | ||||
| 	double sys_adc_ch2_pm3_a53; | ||||
| 	double sys_adc_ch3_pm4_gpu; | ||||
| 	double sys_adc_ch4_vsys; | ||||
| 	double sys_adc_ch5_va57; | ||||
| 	double sys_adc_ch6_va53; | ||||
| 	double sys_adc_ch7_vgpu; | ||||
| 	double sys_pow_ch04_sys; | ||||
| 	double sys_pow_ch15_a57; | ||||
| 	double sys_pow_ch26_a53; | ||||
| 	double sys_pow_ch37_gpu; | ||||
| 	double sys_enm_ch0_sys; | ||||
| 	double sys_enm_ch1_a57; | ||||
| 	double sys_enm_ch0_a53; | ||||
| 	double sys_enm_ch0_gpu; | ||||
| }; | ||||
|  | ||||
| inline uint64_t join_64bit_register(uint32_t *buffer, int index) | ||||
| { | ||||
| 	uint64_t result = 0; | ||||
| 	result |= buffer[index]; | ||||
| 	result |= (uint64_t)(buffer[index+1]) << 32; | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| int nsleep(const struct timespec *req, struct timespec *rem) | ||||
| { | ||||
| 	struct timespec temp_rem; | ||||
| 	if (nanosleep(req, rem) == -1) | ||||
| 	{ | ||||
| 		if (errno == EINTR) | ||||
| 		{ | ||||
| 			nsleep(rem, &temp_rem); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			return errno; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| void print_help() | ||||
| { | ||||
| 	fprintf(stderr, "Usage: readenergy [-t PERIOD] -o OUTFILE\n\n" | ||||
| 			"Read Juno energy counters every PERIOD milliseconds, writing them\n" | ||||
| 			"to OUTFILE in CSV format until SIGTERM is received.\n\n" | ||||
| 			"Parameters:\n" | ||||
| 			"	PERIOD is the counter poll period in milliseconds.\n" | ||||
| 			"	       (Defaults to 100 milliseconds.)\n" | ||||
| 			"	OUTFILE is the output file path\n"); | ||||
| } | ||||
|  | ||||
| // debugging only... | ||||
| inline void dprint(char *msg) | ||||
| { | ||||
| 	fprintf(stderr, "%s\n", msg); | ||||
| 	sync(); | ||||
| } | ||||
|  | ||||
| // -------------------------------------- config ---------------------------------------------------- | ||||
|  | ||||
| struct config | ||||
| { | ||||
| 	struct timespec period; | ||||
| 	char *output_file; | ||||
| }; | ||||
|  | ||||
| void config_init_period_from_millis(struct config *this, long millis) | ||||
| { | ||||
| 	this->period.tv_sec = (time_t)(millis / 1000); | ||||
| 	this->period.tv_nsec = (millis % 1000) * 1000000; | ||||
| } | ||||
|  | ||||
| void config_init(struct config *this, int argc, char *argv[]) | ||||
| { | ||||
| 	this->output_file = NULL; | ||||
| 	config_init_period_from_millis(this, DEFAULT_PERIOD); | ||||
|  | ||||
| 	int opt; | ||||
| 	while ((opt = getopt(argc, argv, "ht:o:")) != -1) | ||||
| 	{ | ||||
| 		switch(opt) | ||||
| 		{ | ||||
| 			case 't': | ||||
| 				config_init_period_from_millis(this, atol(optarg)); | ||||
| 				break; | ||||
| 			case 'o': | ||||
| 				this->output_file = optarg; | ||||
| 				break; | ||||
| 			case 'h': | ||||
| 				print_help(); | ||||
| 				exit(EXIT_SUCCESS); | ||||
| 				break; | ||||
| 			default: | ||||
| 				fprintf(stderr, "ERROR: Unexpected option %s\n\n", opt); | ||||
| 				print_help(); | ||||
| 				exit(EXIT_FAILURE); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (this->output_file == NULL) | ||||
| 	{ | ||||
| 		fprintf(stderr, "ERROR: Mandatory -o option not specified.\n\n"); | ||||
| 		print_help(); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // -------------------------------------- /config --------------------------------------------------- | ||||
|  | ||||
| // -------------------------------------- emeter ---------------------------------------------------- | ||||
|  | ||||
| struct emeter | ||||
| { | ||||
| 	int fd; | ||||
| 	FILE *out; | ||||
| 	void *mmap_base; | ||||
| }; | ||||
|  | ||||
| void emeter_init(struct emeter *this, char *outfile) | ||||
| { | ||||
| 	this->out = fopen(outfile, "w"); | ||||
| 	if (this->out == NULL) | ||||
| 	{ | ||||
| 		fprintf(stderr, "ERROR: Could not open output file %s; got %s\n", outfile, strerror(errno)); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
|         this->fd = open("/dev/mem", O_RDONLY); | ||||
|         if(this->fd < 0) | ||||
|         { | ||||
|                 fprintf(stderr, "ERROR: Can't open /dev/mem; got %s\n", strerror(errno)); | ||||
| 		fclose(this->out); | ||||
| 		exit(EXIT_FAILURE); | ||||
|         } | ||||
|  | ||||
| 	this->mmap_base = mmap(NULL, APB_SIZE, PROT_READ, MAP_SHARED, this->fd, APB_BASE_MEMORY); | ||||
| 	if (this->mmap_base == MAP_FAILED) | ||||
| 	{ | ||||
| 		fprintf(stderr, "ERROR: mmap failed; got %s\n", strerror(errno)); | ||||
| 		close(this->fd); | ||||
| 		fclose(this->out); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
| 	fprintf(this->out, "sys_curr,a57_curr,a53_curr,gpu_curr," | ||||
|  			   "sys_volt,a57_volt,a53_volt,gpu_volt," | ||||
| 			   "sys_pow,a57_pow,a53_pow,gpu_pow," | ||||
| 			   "sys_cenr,a57_cenr,a53_cenr,gpu_cenr\n"); | ||||
| } | ||||
|  | ||||
| void emeter_read_measurements(struct emeter *this, struct reading *reading) | ||||
| { | ||||
| 	uint32_t *buffer = (uint32_t *)this->mmap_base; | ||||
| 	reading->sys_adc_ch0_pm1_sys = (double)(CMASK & buffer[BASE_INDEX+0]) / SYS_ADC_CH0_PM1_SYS_SCALE; | ||||
| 	reading->sys_adc_ch1_pm2_a57 = (double)(CMASK & buffer[BASE_INDEX+1]) / SYS_ADC_CH1_PM2_A57_SCALE; | ||||
| 	reading->sys_adc_ch2_pm3_a53 = (double)(CMASK & buffer[BASE_INDEX+2]) / SYS_ADC_CH2_PM3_A53_SCALE; | ||||
| 	reading->sys_adc_ch3_pm4_gpu = (double)(CMASK & buffer[BASE_INDEX+3]) / SYS_ADC_CH3_PM4_GPU_SCALE; | ||||
| 	reading->sys_adc_ch4_vsys = (double)(VMASK & buffer[BASE_INDEX+4]) / SYS_ADC_CH4_VSYS_SCALE; | ||||
| 	reading->sys_adc_ch5_va57 = (double)(VMASK & buffer[BASE_INDEX+5]) / SYS_ADC_CH5_VA57_SCALE; | ||||
| 	reading->sys_adc_ch6_va53 = (double)(VMASK & buffer[BASE_INDEX+6]) / SYS_ADC_CH6_VA53_SCALE; | ||||
| 	reading->sys_adc_ch7_vgpu = (double)(VMASK & buffer[BASE_INDEX+7]) / SYS_ADC_CH7_VGPU_SCALE; | ||||
| 	reading->sys_pow_ch04_sys = (double)(PMASK & buffer[BASE_INDEX+8]) / SYS_POW_CH04_SYS_SCALE; | ||||
| 	reading->sys_pow_ch15_a57 = (double)(PMASK & buffer[BASE_INDEX+9]) / SYS_POW_CH15_A57_SCALE; | ||||
| 	reading->sys_pow_ch26_a53 = (double)(PMASK & buffer[BASE_INDEX+10]) / SYS_POW_CH26_A53_SCALE; | ||||
| 	reading->sys_pow_ch37_gpu = (double)(PMASK & buffer[BASE_INDEX+11]) / SYS_POW_CH37_GPU_SCALE; | ||||
| 	reading->sys_enm_ch0_sys = (double)join_64bit_register(buffer, BASE_INDEX+12) / SYS_ENM_CH0_SYS_SCALE; | ||||
| 	reading->sys_enm_ch1_a57 = (double)join_64bit_register(buffer, BASE_INDEX+14) / SYS_ENM_CH1_A57_SCALE; | ||||
| 	reading->sys_enm_ch0_a53 = (double)join_64bit_register(buffer, BASE_INDEX+16) / SYS_ENM_CH0_A53_SCALE; | ||||
| 	reading->sys_enm_ch0_gpu = (double)join_64bit_register(buffer, BASE_INDEX+18) / SYS_ENM_CH0_GPU_SCALE; | ||||
| } | ||||
|  | ||||
| void emeter_take_reading(struct emeter *this) | ||||
| { | ||||
| 	static struct reading reading; | ||||
| 	int error_count = 0; | ||||
| 	emeter_read_measurements(this, &reading); | ||||
| 	int ret = fprintf(this->out, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n", | ||||
| 			reading.sys_adc_ch0_pm1_sys, | ||||
| 			reading.sys_adc_ch1_pm2_a57, | ||||
| 			reading.sys_adc_ch2_pm3_a53, | ||||
| 			reading.sys_adc_ch3_pm4_gpu, | ||||
| 			reading.sys_adc_ch4_vsys, | ||||
| 			reading.sys_adc_ch5_va57, | ||||
| 			reading.sys_adc_ch6_va53, | ||||
| 			reading.sys_adc_ch7_vgpu, | ||||
| 			reading.sys_pow_ch04_sys, | ||||
| 			reading.sys_pow_ch15_a57, | ||||
| 			reading.sys_pow_ch26_a53, | ||||
| 			reading.sys_pow_ch37_gpu, | ||||
| 			reading.sys_enm_ch0_sys, | ||||
| 			reading.sys_enm_ch1_a57, | ||||
| 			reading.sys_enm_ch0_a53, | ||||
| 			reading.sys_enm_ch0_gpu); | ||||
| 	if (ret < 0) | ||||
| 	{ | ||||
| 		fprintf(stderr, "ERROR: while writing a meter reading: %s\n", strerror(errno)); | ||||
| 		if (++error_count > ERROR_THRESHOLD) | ||||
| 			exit(EXIT_FAILURE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void emeter_finalize(struct emeter *this) | ||||
| { | ||||
| 	if (munmap(this->mmap_base, APB_SIZE) == -1)  | ||||
| 	{ | ||||
| 		// Report the error but don't bother doing anything else, as we're not gonna do  | ||||
| 		// anything with emeter after this point anyway. | ||||
| 		fprintf(stderr, "ERROR: munmap failed; got %s\n", strerror(errno)); | ||||
| 	} | ||||
| 	close(this->fd); | ||||
| 	fclose(this->out); | ||||
| } | ||||
|  | ||||
| // -------------------------------------- /emeter ---------------------------------------------------- | ||||
|  | ||||
| int done = 0; | ||||
|  | ||||
| void term_handler(int signum) | ||||
| { | ||||
| 	done = 1; | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	struct sigaction action; | ||||
| 	memset(&action, 0, sizeof(struct sigaction)); | ||||
| 	action.sa_handler = term_handler; | ||||
| 	sigaction(SIGTERM, &action, NULL); | ||||
|  | ||||
| 	struct config config; | ||||
| 	struct emeter emeter; | ||||
| 	config_init(&config, argc, argv); | ||||
| 	emeter_init(&emeter, config.output_file); | ||||
|  | ||||
| 	struct timespec remaining; | ||||
| 	while (!done)  | ||||
| 	{ | ||||
| 		emeter_take_reading(&emeter); | ||||
| 		nsleep(&config.period, &remaining); | ||||
| 	} | ||||
|  | ||||
| 	emeter_finalize(&emeter); | ||||
| 	return EXIT_SUCCESS; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user