aboutsummaryrefslogtreecommitdiff
path: root/arm-probe
diff options
context:
space:
mode:
authorAndy Green <andy.green@linaro.org>2012-10-06 12:51:05 +0800
committerAndy Green <andy.green@linaro.org>2012-10-06 12:51:05 +0800
commit7865c5bd9a6dca340b400b754b9d87e11d7367c2 (patch)
tree987dfb46470ce412d0d6f830facab5e2b1f0d0a1 /arm-probe
parentb8bd63c81b0082e40bb6d7ea59f0523abbab07a7 (diff)
introduce autotools libarmep
Signed-off-by: Andy Green <andy.green@linaro.org>
Diffstat (limited to 'arm-probe')
-rw-r--r--arm-probe/Makefile.am6
-rw-r--r--arm-probe/arm-probe.c495
2 files changed, 501 insertions, 0 deletions
diff --git a/arm-probe/Makefile.am b/arm-probe/Makefile.am
new file mode 100644
index 0000000..426ff71
--- /dev/null
+++ b/arm-probe/Makefile.am
@@ -0,0 +1,6 @@
+bin_PROGRAMS=arm-probe
+arm_probe_SOURCES=arm-probe.c
+arm_probe_CFLAGS=-fPIC -Wall -std=gnu99 -pedantic
+arm_probe_LDFLAGS=-fPIC
+arm_probe_LDADD=-L../libarmep -larmep
+
diff --git a/arm-probe/arm-probe.c b/arm-probe/arm-probe.c
new file mode 100644
index 0000000..1d8c6c7
--- /dev/null
+++ b/arm-probe/arm-probe.c
@@ -0,0 +1,495 @@
+/*
+ * Author: Daniel Lezcano <daniel.lezcano@linaro.org> (Initial version)
+ * : Andy Green <andy.green@linaro.org>
+ * Copyright (C) 2012 Linaro, LTD
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "../libarmep/libarmep.h"
+
+int just_power = 0;
+char stdinbuf[512];
+char stdinline[512] = "";
+int stdinpos = 0;
+int stdinlen = 0;
+double do_average = 0;
+
+static void issue_samples(enum samples_actions sa, struct aep_channel *ch,
+ double voltage, double current);
+
+
+struct aep_context aep_context = {
+ .samples = issue_samples,
+ .config_filepath = "./config",
+ .highest = -1,
+ .decimate = 1,
+ .mv_min = 400,
+ .trigger_filter_us = 400,
+ .end_trigger_filter_us = 500000,
+ .average_len = 1,
+ .configuration_name = "default_configuration",
+};
+
+void pull_stdin_if_needed(void)
+{
+ int n;
+
+ if (stdinpos != stdinlen)
+ return;
+
+ n = read(0, stdinbuf, sizeof stdinbuf);
+ if (n < 0) {
+ if (aep_context.verbose)
+ fprintf(stderr, "pull_stdin_if_needed: n=%d\n", n);
+ return;
+ }
+ if (n == 0 && aep_context.verbose)
+ fprintf(stderr, "pull_stdin_if_needed n = 0\n");
+ stdinpos = 0;
+ stdinlen = n;
+}
+
+int get_stdin_line(char *buf, int maxlen)
+{
+ while (maxlen--) {
+ if (stdinpos == stdinlen) {
+ pull_stdin_if_needed();
+ if (stdinpos == stdinlen)
+ return -1;
+ }
+ *buf++ = stdinbuf[stdinpos++];
+ if (buf[-1] == '\n') {
+ buf[-1] = '\0';
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/*
+ * this is the callback where the results appear
+ */
+
+static void issue_samples(enum samples_actions sa, struct aep_channel *ch,
+ double voltage, double current)
+{
+ static int periodic;
+ static char titles[4096];
+ static int pos;
+
+ switch (sa) {
+
+ case AEPSA_DATA_STARTING:
+ printf("#\n");
+ pos = 0;
+ if (!stdinline[0])
+ break;
+ printf("%s", stdinline);
+ get_stdin_line(stdinline, sizeof stdinline);
+
+ break;
+ case AEPSA_DATA_STARTING_CHANNELS:
+
+ printf("# %s\t%s\t%s\n",
+ ch->channel_name, ch->channel_name_pretty, ch->supply);
+
+ if (just_power)
+ pos += sprintf(&titles[pos], " %s(W)",
+ ch->channel_name_pretty);
+ else
+ pos += sprintf(&titles[pos]," %s(V) %s(A) %s(W)",
+ ch->channel_name_pretty,
+ ch->channel_name_pretty,
+ ch->channel_name_pretty);
+ break;
+
+ case AEPSA_DATA_STARTING_DONE:
+ printf("#\nsample");
+ puts(titles);
+ break;
+
+ case AEPSA_START_PRE:
+ periodic++;
+ if (periodic & 0x1ff)
+ break;
+ fprintf(stderr, "ARMED ");
+ break;
+ case AEPSA_SAMPLE_PRE:
+ if (periodic & 0x1ff)
+ break;
+ if (just_power)
+ fprintf(stderr, " %.5f", voltage * current);
+ else
+ fprintf(stderr, " %.2f %.4f %.5f",
+ voltage, current, voltage * current);
+ break;
+ case AEPSA_END_PRE:
+ if (periodic & 0x1ff)
+ break;
+ fprintf(stderr, "\n");
+ break;
+
+ case AEPSA_START:
+ periodic++;
+ if (stdinline[0]) {
+ printf("%s", stdinline);
+ if (get_stdin_line(stdinline, sizeof stdinline))
+ stdinline[0] = '\0';
+ } else
+ printf("%f", voltage);
+ if (periodic & 0x1ff)
+ break;
+ fprintf(stderr, "TRIGD ");
+ break;
+ case AEPSA_SAMPLE:
+ if (just_power)
+ printf(" %.5f", voltage * current);
+ else
+ printf(" %.2f %.4f %.5f",
+ voltage, current, voltage * current);
+ if (periodic & 0x1ff)
+ break;
+ if (just_power)
+ fprintf(stderr, " %.5f", voltage * current);
+ else
+ fprintf(stderr, " %.2f %.4f %.5f",
+ voltage, current, voltage * current);
+ break;
+ case AEPSA_END:
+ printf("\n");
+ if (periodic & 0x1ff)
+ break;
+ fprintf(stderr, "\n");
+ break;
+ }
+}
+
+
+
+
+static struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "raw", no_argument, NULL, 'r' },
+ { "mean", required_argument, NULL, 'm' },
+ { "decimate", required_argument, NULL, 'd' },
+ { "mvtrigger", required_argument, NULL, 'q' },
+ { "mwtrigger", required_argument, NULL, 'w' },
+ { "channel", required_argument, NULL, 'c' },
+ { "mstrighold", required_argument, NULL, 't' },
+ { "ustrigfilter", required_argument, NULL, 'f' },
+ { "mslength", required_argument, NULL, 'l' },
+ { "exitoncap", no_argument, NULL, 'x' },
+ { "justpower", no_argument, NULL, 'j' },
+ { "config", required_argument, NULL, 'C' },
+ { "offexit", no_argument, NULL, 'o' },
+ { "mspretrigger", required_argument, NULL, 'p' },
+ { "nocorrection", no_argument, NULL, 'n' },
+ { "autozero", no_argument, NULL, 'z' },
+ { "average", required_argument, NULL, 'a' },
+ { NULL, 0, 0, 0 }
+};
+
+static int loop;
+
+void sighandler(int sig)
+{
+ loop = 0;
+}
+
+int main(int argc, char *argv[])
+{
+ time_t t;
+ struct tm *tmp;
+ char hostname[200];
+ char date[200];
+ int n = 0;
+ struct aep_channel *ch;
+ double tt;
+ int m;
+ int i;
+
+ loop = 1;
+
+ signal(SIGINT, sighandler);
+
+ while (n >= 0) {
+ n = getopt_long(argc, argv, "xl:t:c:q:hvm:jC:w:f:op:rd:nza:",
+ options, NULL);
+ if (n < 0)
+ continue;
+ switch (n) {
+ case 'a':
+ do_average = atof(optarg);
+ break;
+ case 'z':
+ fprintf(stderr, "\n\n *** Note: for autozero, you "
+ "should short both sides of the "
+ "\n channel sense "
+ "leads to 0V... in some cases a powered-down "
+ "target will do ***\n\n");
+ aep_context.no_correction = 1;
+ aep_context.mv_min = 0;
+ aep_context.mw_min = 0;
+ aep_context.auto_zero = 1;
+ aep_context.average_len = 50000;
+ aep_context.ms_capture = aep_context.average_len / 10;
+ aep_context.decimate = aep_context.average_len;
+ aep_context.exit_after_capture = 1;
+ break;
+ case 'n':
+ aep_context.no_correction = 1;
+ break;
+ case 'd':
+ aep_context.decimate = atoi(optarg);
+ break;
+ case 'r':
+ aep_context.show_raw = 1;
+ break;
+ case 'p':
+ aep_context.ms_pretrigger = atoi(optarg);
+ break;
+ case 'o':
+ aep_context.require_off = 1;
+ break;
+ case 'f':
+ aep_context.trigger_filter_us = atoi(optarg);
+ break;
+ case 'w':
+ aep_context.mw_min = atoi(optarg);
+ if (aep_context.mw_min)
+ aep_context.mw_min_plus_hyst =
+ aep_context.mw_min + (aep_context.mw_min / 10);
+ break;
+ case 'j':
+ just_power = 1;
+ break;
+ case 'C':
+ strncpy(aep_context.config_filepath, optarg,
+ sizeof aep_context.config_filepath - 1);
+ aep_context.config_filepath[
+ sizeof aep_context.config_filepath - 1] = '\0';
+ break;
+ case 't':
+ aep_context.ms_holdoff = atoi(optarg);
+ break;
+ case 'l':
+ aep_context.ms_capture = atoi(optarg);
+ break;
+ case 'c':
+ if (aep_context.count_channel_names == MAX_PROBES) {
+ fprintf(stderr, "too many channels named\n");
+ return -1;
+ }
+ strncpy(aep_context.channel_name[aep_context.count_channel_names], optarg,
+ sizeof aep_context.channel_name[0] - 1);
+ aep_context.channel_name[aep_context.count_channel_names]
+ [sizeof aep_context.channel_name[0] - 1] = '\0';
+ aep_context.count_channel_names++;
+ break;
+ case 'q':
+ aep_context.mv_min = atoi(optarg);
+ break;
+ case 'm':
+ aep_context.average_len = atoi(optarg);
+ break;
+ case 'v':
+ aep_context.verbose++;
+ break;
+ case 'x':
+ aep_context.exit_after_capture = 1;
+ break;
+ default:
+ case 'h':
+ fprintf(stderr, "Usage: arm-probe \n"
+ " [--verbose -v] Increase debug on stderr\n"
+ " [--config -C <file>] use alternate config "
+ "(default ./config)\n"
+ " [--mean -m <averagng depth>] (default 1)\n"
+ " [--decimate -d <report interval>] only "
+ "output every n samples\n"
+ " [--mvtrigger -q <min mV>] suppress output "
+ "below this voltage (defaut 400mV)\n"
+ " [--mwtrigger -w <min mW>] suppress output "
+ "below this power (default 0mW)\n"
+ " [--channel -c <name>] select channel\n"
+ " [--mstrighold -t <ms>] delay in ms after "
+ "power seen before capture\n"
+ " [--ustrigfilter -f <us>] require trigger "
+ "threshold to be exceeded for at "
+ "least this amount of time\n"
+ " [--mspretrigger -p <ms>] at trigger, issue "
+ "this many samples from BEFORE "
+ "trigger first\n"
+ " [--length -l <ms>] period to capture after "
+ "trigger (+holdoff if any)\n"
+ " [--exitoncap -x] after requested capture "
+ "(-l) completes on all channels, "
+ "exit app\n"
+ " [--offexit -o] wait for voltage to reduce "
+ "below trigger before exiting\n"
+ " [--justpower -j] just write power\n"
+ " [--autozero -z] measure channel and store "
+ "result as zero offset\n"
+ " [--average -a <float>] at the end of the "
+ "capture, add a simple mean average "
+ "to the \n"
+ " stdout dataset, starting at "
+ "<float> seconds\n"
+ );
+ exit(1);
+ }
+ }
+
+ aep_context.original_count_channel_names = aep_context.count_channel_names;
+
+ if (!isatty(0)) {
+
+ m = 1;
+ n = 0;
+ while (m) {
+ n++;
+ if (n > 1000) {
+ fprintf(stderr, "problem with stdin format\n");
+ return 1;
+ }
+ if (get_stdin_line(stdinline, sizeof stdinline)) {
+ fprintf(stderr, "get_stdin_line failed\n");
+ m = 0;
+ continue;
+ }
+ if (stdinline[0] != '#') {
+ if (aep_context.verbose)
+ fprintf(stderr, "seen '%s'\n", stdinline);
+ puts("#");
+ m = 0;
+ continue;
+ }
+ puts(stdinline);
+ }
+ }
+
+ configure(&aep_context, NULL, "xx", aep_context.config_filepath, NULL);
+
+ printf("# configuration: %s\n", aep_context.config_filepath);
+ printf("# config_name: %s\n", aep_context.configuration_name);
+ printf("# trigger: %fV (hyst %fV) %fW (hyst %fW) %uus\n",
+ aep_context.mv_min / 1000.0, TRIG_HYSTERESIS_MV / 1000.0,
+ aep_context.mw_min / 1000.0, TRIG_HYSTERESIS_MV / 1000.0,
+ aep_context.trigger_filter_us);
+
+ fprintf(stderr, "Configuration: %s\n", aep_context.configuration_name);
+
+ t = time(NULL);
+ tmp = localtime(&t);
+ if (tmp) {
+ if (strftime(date, sizeof date, "%a, %d %b %Y %T %z", tmp))
+ printf("# date: %s\n", date);
+ }
+
+ if (!gethostname(hostname, sizeof hostname))
+ printf("# host: %s\n", hostname);
+
+ printf("#\n");
+
+ /*
+ * the main service loop
+ */
+
+ while (loop)
+ if (service_aeps(&aep_context) < 0)
+ loop = 0;
+
+ /*
+ * we are finished if we reach here...
+ * append simple average to results if requested
+ */
+
+ if (aep_context.awaiting_capture == 0 &&
+ aep_context.exit_after_capture && do_average) {
+
+ tt = do_average;
+
+ for (m = 0; m < 2; m++) {
+
+ if (stdinline[0]) {
+ printf("%s", stdinline);
+ if (get_stdin_line(stdinline, sizeof stdinline))
+ stdinline[0] = '\0';
+ } else
+ printf("%f", tt);
+
+ for (n = 0; n <= aep_context.highest; n++) {
+ for (i = 0; i < CHANNELS_PER_PROBE; i++) {
+ ch = &aep_context.aeps[n].ch[i];
+ if (just_power)
+ printf(" %.5f", ch->simple_avg[2] / ch->avg_count);
+ else
+ printf(" %.2f %.4f %.5f",
+ ch->simple_avg[0] / ch->avg_count,
+ ch->simple_avg[1] / ch->avg_count,
+ ch->simple_avg[2] / ch->avg_count);
+ }
+ }
+ printf("\n");
+
+ tt += do_average / 3;
+ }
+ }
+
+ /*
+ * dump a summary of extents and mean of all channels now we are done
+ */
+
+ fprintf(stderr, "\n\n");
+
+ for (m = 0; m <= aep_context.highest; m++) {
+ int i;
+
+ if (aep_context.aeps[m].fd <= 0)
+ continue;
+
+
+ for (i = 0; i < CHANNELS_PER_PROBE; i++) {
+ ch = &aep_context.aeps[m].ch[i];
+ if (configure(&aep_context, &aep_context.aeps[m],
+ aep_context.aeps[m].dev_filepath,
+ aep_context.config_filepath, ch) < 0)
+ fprintf(stderr, "failed to update config\n");
+
+ if (ch->min[0] == 999)
+ continue;
+
+ fprintf(stderr, "%12s: %4.2fV < %4.3fVavg < %4.2fV, "
+ "%6.4fA < %6.5fAavg < %6.4fA, "
+ "%9.6fW < %9.6fWavg < %9.6fW\n",
+ ch->channel_name, ch->min[0],
+ ch->simple_avg[0] / ch->avg_count, ch->max[0],
+ ch->min[1],
+ ch->simple_avg[1] / ch->avg_count, ch->max[1],
+ ch->min[2],
+ ch->simple_avg[2] / ch->avg_count, ch->max[2]);
+ }
+ probe_close(&aep_context.aeps[m]);
+ }
+
+ fprintf(stderr, "exited\n");
+
+ return 0;
+}
+