• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
join_64bit_register(uint32_t * buffer,int index)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 
nsleep(const struct timespec * req,struct timespec * rem)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 
print_help()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...
dprint(char * msg)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 
config_init_period_from_millis(struct config * this,long millis)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 
config_init(struct config * this,int argc,char * argv[])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 
emeter_init(struct emeter * this,char * outfile)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 
emeter_read_measurements(struct emeter * this,struct reading * reading)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 
emeter_take_reading(struct emeter * this)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 
emeter_finalize(struct emeter * this)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 
term_handler(int signum)331 void term_handler(int signum)
332 {
333 	done = 1;
334 }
335 
sigalrm_handler(int signum)336 void sigalrm_handler(int signum)
337 {
338 	done = 1;
339 }
340 
341 
main(int argc,char * argv[])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