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