From cc5aed4c6dc3877ae237d3c4ee7c463ce3fbe4bb Mon Sep 17 00:00:00 2001 From: Daniel Beer Date: Wed, 3 Oct 2012 16:28:51 +1300 Subject: [PATCH] Power history support. This adds a "power" command which can be used to dump power history samples gathered by the device driver. --- Makefile | 2 + drivers/device.h | 6 + drivers/fet_core.c | 136 +++++++++++++++++++++- ui/cmddb.c | 14 +++ ui/power.c | 191 +++++++++++++++++++++++++++++++ ui/power.h | 24 ++++ util/powerbuf.c | 279 +++++++++++++++++++++++++++++++++++++++++++++ util/powerbuf.h | 123 ++++++++++++++++++++ 8 files changed, 771 insertions(+), 4 deletions(-) create mode 100644 ui/power.c create mode 100644 ui/power.h create mode 100644 util/powerbuf.c create mode 100644 util/powerbuf.h diff --git a/Makefile b/Makefile index bec1c26..5f64236 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,7 @@ OBJ=\ util/gdb_proto.o \ util/dynload.o \ util/demangle.o \ + util/powerbuf.o \ transport/cp210x.o \ transport/cdc_acm.o \ transport/ftdi.o \ @@ -157,6 +158,7 @@ OBJ=\ ui/cmddb.o \ ui/stdcmd.o \ ui/aliasdb.o \ + ui/power.o \ ui/main.o $(BINARY): $(OBJ) diff --git a/drivers/device.h b/drivers/device.h index 10fd416..5b74efd 100644 --- a/drivers/device.h +++ b/drivers/device.h @@ -21,6 +21,7 @@ #include #include "util.h" +#include "powerbuf.h" struct device; typedef struct device *device_t; @@ -122,6 +123,11 @@ struct device { */ int max_breakpoints; struct device_breakpoint breakpoints[DEVICE_MAX_BREAKPOINTS]; + + /* Power sample buffer, if power profiling is supported by this + * device. + */ + powerbuf_t power_buf; }; /* Probe the device memory and extract ID bytes. This should be called diff --git a/drivers/fet_core.c b/drivers/fet_core.c index a033cc7..e560302 100644 --- a/drivers/fet_core.c +++ b/drivers/fet_core.c @@ -46,6 +46,8 @@ struct fet_device { int version; int fet_flags; + int poll_enable; + struct fet_proto proto; }; @@ -97,6 +99,10 @@ struct fet_device { #define C_IDENT2 0x29 #define C_IDENT3 0x2b +#define C_CMM_PARAM 0x36 +#define C_CMM_CTRL 0x37 +#define C_CMM_READ 0x38 + /* Constants for parameters of various FET commands */ #define FET_CONFIG_VERIFICATION 0 #define FET_CONFIG_EMULATION 1 @@ -380,6 +386,105 @@ static int do_identify(struct fet_device *dev, const char *force_id) return identify_new(dev, force_id); } +static void power_init(struct fet_device *dev) +{ + if (fet_proto_xfer(&dev->proto, C_CMM_PARAM, NULL, 0, 0) < 0) { + printc_err("warning: device does not support power " + "profiling\n"); + return; + } + + if (dev->proto.argv[0] <= 0 || dev->proto.argv[0] <= 0) { + printc_err("Bad parameters returned by C_CMM_PARAM: " + "bufsize = %d bytes, %d us/sample\n", + dev->proto.argv[1], dev->proto.argv[0]); + return; + } + + printc("Power profiling enabled: bufsize = %d bytes, %d us/sample\n", + dev->proto.argv[1], dev->proto.argv[0]); + + dev->base.power_buf = powerbuf_new(POWERBUF_DEFAULT_SAMPLES, + dev->proto.argv[0]); + if (!dev->base.power_buf) { + printc_err("Failed to allocate memory for power profile\n"); + return; + } +} + +static int power_start(struct fet_device *dev) +{ + if (!dev->base.power_buf) + return 0; + + if (fet_proto_xfer(&dev->proto, C_CMM_CTRL, NULL, 0, 1, 1) < 0) { + printc_err("fet: failed to start power profiling, " + "disabling\n"); + powerbuf_free(dev->base.power_buf); + dev->base.power_buf = NULL; + return -1; + } + + powerbuf_begin_session(dev->base.power_buf, time(NULL)); + dev->poll_enable = 1; + return 0; +} + +static int power_end(struct fet_device *dev) +{ + if (!dev->base.power_buf) + return 0; + + powerbuf_end_session(dev->base.power_buf); + dev->poll_enable = 0; + + if (fet_proto_xfer(&dev->proto, C_CMM_CTRL, NULL, 0, 1, 1) < 0) { + printc_err("fet: failed to end power profiling\n"); + return -1; + } + + return 0; +} + +static int power_poll(struct fet_device *dev) +{ + address_t mab; + address_t mab_samples[1024]; + unsigned int cur_samples[1024]; + unsigned int count = 0; + int i; + + if (!dev->base.power_buf || !dev->poll_enable) + return 0; + + if (fet_proto_xfer(&dev->proto, C_CMM_READ, NULL, 0, 0) < 0) { + printc_err("fet: failed to fetch power data, disabling\n"); + power_end(dev); + powerbuf_free(dev->base.power_buf); + dev->base.power_buf = NULL; + dev->poll_enable = 0; + return -1; + } + + mab = powerbuf_last_mab(dev->base.power_buf); + for (i = 0; i + 3 < dev->proto.datalen; i += 4) { + uint32_t s = LE_LONG(dev->proto.data, i); + + if (s & 0x80000000) { + mab = s & 0x7fffffff; + } else if (count + 1 < ARRAY_LEN(cur_samples)) { + cur_samples[count] = s; + mab_samples[count] = mab; + count++; + } + } + + powerbuf_add_samples(dev->base.power_buf, count, + cur_samples, mab_samples); + + return 0; +} + static int do_run(struct fet_device *dev, int type) { if (fet_proto_xfer(&dev->proto, C_RUN, NULL, 0, 2, type, 0) < 0) { @@ -443,16 +548,25 @@ device_status_t fet_poll(device_t dev_base) struct fet_device *dev = (struct fet_device *)dev_base; ctrlc_reset(); - if ((delay_ms(50) < 0) || ctrlc_check()) - return DEVICE_STATUS_INTR; if (fet_proto_xfer(&dev->proto, C_STATE, NULL, 0, 1, 0) < 0) { printc_err("fet: polling failed\n"); + power_end(dev); return DEVICE_STATUS_ERROR; } - if (!(dev->proto.argv[0] & FET_POLL_RUNNING)) + if (dev->base.power_buf) + power_poll(dev); + else + delay_ms(50); + + if (!(dev->proto.argv[0] & FET_POLL_RUNNING)) { + power_end(dev); return DEVICE_STATUS_HALTED; + } + + if (ctrlc_check()) + return DEVICE_STATUS_INTR; return DEVICE_STATUS_RUNNING; } @@ -503,9 +617,17 @@ int fet_ctl(device_t dev_base, device_ctl_t action) if (refresh_bps(dev) < 0) printc_err("warning: fet: failed to refresh " "breakpoints\n"); - return do_run(dev, FET_RUN_BREAKPOINT); + + power_start(dev); + if (do_run(dev, FET_RUN_BREAKPOINT) < 0) { + power_end(dev); + return -1; + } + + return 0; case DEVICE_CTL_HALT: + power_end(dev); if (fet_proto_xfer(&dev->proto, C_STATE, NULL, 0, 1, 1) < 0) { printc_err("fet: failed to halt CPU\n"); return -1; @@ -550,6 +672,9 @@ void fet_destroy(device_t dev_base) if (fet_proto_xfer(&dev->proto, C_CLOSE, NULL, 0, 1, 0) < 0) printc_err("fet: close command failed\n"); + if (dev->base.power_buf) + powerbuf_free(dev->base.power_buf); + dev->proto.transport->ops->destroy(dev->proto.transport); free(dev); } @@ -853,6 +978,9 @@ device_t fet_open(const struct device_args *args, for (i = 0; i < dev->base.max_breakpoints; i++) dev->base.breakpoints[i].flags = DEVICE_BP_DIRTY; + /* Initialize power profiling */ + power_init(dev); + return (device_t)dev; fail: diff --git a/ui/cmddb.c b/ui/cmddb.c index afe5bb1..c3957e0 100644 --- a/ui/cmddb.c +++ b/ui/cmddb.c @@ -29,6 +29,7 @@ #include "stdcmd.h" #include "simio.h" #include "aliasdb.h" +#include "power.h" const struct cmddb_record commands[] = { { @@ -332,6 +333,19 @@ const struct cmddb_record commands[] = { .help = "fill
[b1 b2 ...]\n" " Fill the given memory range with a repeated byte sequence.\n" + }, + { + .name = "power", + .func = cmd_power, + .help = +"power info\n" +" Show basic power statistics.\n" +"power clear\n" +" Clear power statistics.\n" +"power all [granularity]\n" +" Show all power data, optionally specifying a granularity in us.\n" +"power session [granularity]\n" +" Show data only for the specified session.\n" } }; diff --git a/ui/power.c b/ui/power.c new file mode 100644 index 0000000..66a6cbe --- /dev/null +++ b/ui/power.c @@ -0,0 +1,191 @@ +/* MSPDebug - debugging tool MSP430 MCUs + * Copyright (C) 2009-2012 Daniel Beer + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "util.h" +#include "output.h" +#include "output_util.h" +#include "device.h" +#include "power.h" +#include "powerbuf.h" + +static void print_header(powerbuf_t pb, unsigned int s) +{ + unsigned int length; + const struct powerbuf_session *rec = + powerbuf_session_info(pb, s, &length); + + printc("Session #%d: %s", s, ctime(&rec->wall_clock)); + printc("%d samples (spanning %.03f ms)\n", + length, (double)(length * pb->interval_us) / 1000.0); + printc("%.01f uA average (%.01f uAs total charge)\n", + (double)rec->total_ua / (double)length, + (double)(rec->total_ua * pb->interval_us) / 1000000.0); +} + +static void dump_session_data(powerbuf_t pb, unsigned int s, + unsigned int gran) +{ + unsigned int length; + const struct powerbuf_session *rec = + powerbuf_session_info(pb, s, &length); + unsigned int i; + int idx; + + print_header(pb, s); + printc("\n"); + + printc("%15s %15s %-15s\n", "Time (us)", "Current (uA)", "MAB"); + printc("------------------------------------------------\n"); + + idx = rec->start_index; + + for (i = 0; i + gran <= length; i += gran) { + address_t mab = pb->mab[idx]; + unsigned long ua_tot = 0; + char addr[128]; + int j; + + for (j = 0; j < gran; j++) { + ua_tot += pb->current_ua[idx]; + idx = (idx + 1) % pb->max_samples; + } + + print_address(mab, addr, sizeof(addr)); + printc("%15d %15.01f %s\n", i * pb->interval_us, + ((double)ua_tot) / (double)gran, addr); + } + + printc("\n"); +} + +static int sc_info(powerbuf_t pb) +{ + int sess_num = powerbuf_num_sessions(pb); + int i; + + printc("Sample granularity is %d us\n", pb->interval_us); + printc("%d sessions:\n", sess_num); + + for (i = sess_num - 1; i >= 0; i--) { + printc("\n"); + print_header(pb, i); + } + + return 0; +} + +static int sc_clear(powerbuf_t pb) +{ + powerbuf_clear(pb); + return 0; +} + +static int parse_granularity(powerbuf_t pb, char **arg, int *gran_out) +{ + const char *text = get_arg(arg); + int request = 10000; + int gran; + + if (text) + request = atoi(text); + + if (request <= 0) { + printc_err("power: invalid granularity: %d us\n", request); + return -1; + } + + gran = (request + (pb->interval_us / 2)) / pb->interval_us; + + if (gran <= 0) + gran = 1; + + *gran_out = gran; + return 0; +} + +static int sc_all(powerbuf_t pb, char **arg) +{ + int i; + int gran; + + if (parse_granularity(pb, arg, &gran) < 0) + return -1; + + for (i = powerbuf_num_sessions(pb) - 1; i >= 0; i--) + dump_session_data(pb, i, gran); + + return 0; +} + +static int sc_session(powerbuf_t pb, char **arg) +{ + const char *sess_text = get_arg(arg); + int sess; + int gran; + + if (!sess_text) { + printc_err("power: you must specify a session number\n"); + return -1; + } + + sess = atoi(sess_text); + if (sess < 0 || sess >= powerbuf_num_sessions(pb)) { + printc_err("power: invalid session: %d\n", sess); + return -1; + } + + if (parse_granularity(pb, arg, &gran) < 0) + return -1; + + dump_session_data(pb, sess, gran); + return 0; +} + +int cmd_power(char **arg) +{ + powerbuf_t pb = device_default->power_buf; + char *subcmd = get_arg(arg); + + if (!pb) { + printc_err("power: power profiling is not supported " + "by this device.\n"); + return -1; + } + + if (!subcmd) { + printc_err("power: need to specify a subcommand " + "(try \"help power\")\n"); + return -1; + } + + if (!strcasecmp(subcmd, "info")) + return sc_info(pb); + if (!strcasecmp(subcmd, "clear")) + return sc_clear(pb); + if (!strcasecmp(subcmd, "all")) + return sc_all(pb, arg); + if (!strcasecmp(subcmd, "session")) + return sc_session(pb, arg); + + printc_err("power: unknown subcommand: %s (try \"help power\")\n", + subcmd); + return -1; +} diff --git a/ui/power.h b/ui/power.h new file mode 100644 index 0000000..f0d4b4c --- /dev/null +++ b/ui/power.h @@ -0,0 +1,24 @@ +/* MSPDebug - debugging tool MSP430 MCUs + * Copyright (C) 2009-2012 Daniel Beer + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef POWER_H_ +#define POWER_H_ + +int cmd_power(char **arg); + +#endif diff --git a/util/powerbuf.c b/util/powerbuf.c new file mode 100644 index 0000000..17ee55f --- /dev/null +++ b/util/powerbuf.c @@ -0,0 +1,279 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2012 Daniel Beer + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "powerbuf.h" + +powerbuf_t powerbuf_new(unsigned int max_samples, unsigned int interval_us) +{ + powerbuf_t pb = malloc(sizeof(*pb)); + + if (!pb) + return NULL; + + if (pb->max_samples <= 0) { + free(pb); + return NULL; + } + + memset(pb, 0, sizeof(*pb)); + + pb->current_ua = malloc(sizeof(pb->current_ua[0]) * max_samples); + if (!pb->current_ua) { + free(pb); + return NULL; + } + + pb->mab = malloc(sizeof(pb->mab[0]) * max_samples); + if (!pb->mab) { + free(pb->current_ua); + free(pb->mab); + return NULL; + } + + pb->interval_us = interval_us; + pb->max_samples = max_samples; + + pb->session_head = pb->session_tail = 0; + pb->current_head = pb->current_tail = 0; + + return pb; +} + +void powerbuf_free(powerbuf_t pb) +{ + free(pb->current_ua); + free(pb->mab); + free(pb); +} + +void powerbuf_clear(powerbuf_t pb) +{ + pb->session_head = pb->session_tail = 0; + pb->current_head = pb->current_tail = 0; +} + +static unsigned int session_length(powerbuf_t pb, unsigned int idx) +{ + const unsigned int next_idx = (idx + 1) % POWERBUF_MAX_SESSIONS; + unsigned int end_index = pb->current_head; + + /* If a session follows this one, the end index is the start + * index of the following session. Otherwise, it's the end index + * of the entire current/MAB buffer. + */ + if (next_idx != pb->session_head) + end_index = pb->sessions[next_idx].start_index; + + /* Return (end-start) modulo max_samples */ + return (end_index + pb->max_samples - pb->sessions[idx].start_index) % + pb->max_samples; +} + +static void pop_oldest_session(powerbuf_t pb) +{ + unsigned int length = session_length(pb, pb->session_tail); + + /* Remove corresponding samples from the tail of the current/MAB + * buffers. + */ + pb->current_tail = (pb->current_tail + length) % pb->max_samples; + + /* Remove the session from the session buffer. */ + pb->session_tail = (pb->session_tail + 1) % POWERBUF_MAX_SESSIONS; +} + +void powerbuf_begin_session(powerbuf_t pb, time_t when) +{ + struct powerbuf_session *s; + unsigned int next_head; + + /* If the most recent session is empty, remove it */ + powerbuf_end_session(pb); + + /* If the session buffer is full, drop the oldest */ + next_head = (pb->session_head + 1) % POWERBUF_MAX_SESSIONS; + if (next_head == pb->session_tail) + pop_oldest_session(pb); + + /* Copy data to the head */ + s = &pb->sessions[pb->session_head]; + s->wall_clock = when; + s->start_index = pb->current_head; + s->total_ua = 0; + + /* Advance the head pointer */ + pb->session_head = next_head; +} + +/* Return the index of the nth most recent session */ +static unsigned int rev_index(powerbuf_t pb, unsigned int n) +{ + return (pb->session_head + POWERBUF_MAX_SESSIONS - 1 - n) % + POWERBUF_MAX_SESSIONS; +} + +void powerbuf_end_session(powerbuf_t pb) +{ + /* (head-1) modulo MAX_SESSIONS */ + const unsigned int last_idx = rev_index(pb, 0); + + /* If there are no sessions, do nothing */ + if (pb->session_head == pb->session_tail) + return; + + /* If no samples were added since the session began, decrement + * the head pointer. + */ + if (pb->sessions[last_idx].start_index == pb->current_head) + pb->session_head = last_idx; +} + +unsigned int powerbuf_num_sessions(powerbuf_t pb) +{ + /* (head-tail) modulo MAX_SESSIONS */ + return (pb->session_head + POWERBUF_MAX_SESSIONS - pb->session_tail) % + POWERBUF_MAX_SESSIONS; +} + +const struct powerbuf_session *powerbuf_session_info(powerbuf_t pb, + unsigned int rev_idx, unsigned int *length) +{ + /* (head-1-rev_idx) modulo MAX_SESSIONS */ + const unsigned int idx_map = rev_index(pb, rev_idx); + + if (length) + *length = session_length(pb, idx_map); + + return &pb->sessions[idx_map]; +} + +static void ensure_room(powerbuf_t pb, unsigned int required) +{ + unsigned int room = + (pb->current_tail + pb->max_samples - pb->current_head - 1) % + pb->max_samples; + + /* Drop old sessions if they're smaller than what we need to + * reclaim. + */ + while (room < required && powerbuf_num_sessions(pb) > 1) { + const unsigned int len = session_length(pb, pb->session_tail); + + if (room + len > required) + break; + + pop_oldest_session(pb); + room += len; + } + + /* If we still lack space, it must be because the oldest session + * is larger than what we still need to reclaim (we'll never be + * asked to reclaim more than the buffer can hold). + * + * We also know at this point that (required-room) is <= the + * length of the oldest session. + */ + while (room < required) { + struct powerbuf_session *old = + &pb->sessions[pb->session_tail]; + unsigned int cont_len = pb->max_samples - old->start_index; + int i; + + if (cont_len + room > required) + cont_len = required - room; + + /* Un-integrate current */ + for (i = 0; i < cont_len; i++) + old->total_ua -= + pb->current_ua[old->start_index + i]; + + /* Advance the start index and buffer tail */ + old->start_index = (old->start_index + cont_len) % + pb->max_samples; + pb->current_tail = (pb->current_tail + cont_len) % + pb->max_samples; + + room += cont_len; + } +} + +void powerbuf_add_samples(powerbuf_t pb, unsigned int count, + const unsigned int *current_ua, + const address_t *mab) +{ + struct powerbuf_session *cur = &pb->sessions[rev_index(pb, 0)]; + int i; + + /* If no session is active, do nothing */ + if (pb->session_head == pb->session_tail) + return; + + /* Make sure that we can't overflow the buffer in a single + * chunk. + */ + if (count > pb->max_samples - 1) { + int extra = pb->max_samples - 1 - count; + + current_ua += extra; + mab += extra; + count -= extra; + } + + /* Push old samples/sessions out of the buffer if we need to. */ + ensure_room(pb, count); + + /* Add current integral to the session's running count */ + for (i = 0; i < count; i++) + cur->total_ua += current_ua[i]; + + /* Add samples in contiguous chunks */ + while (count) { + unsigned int cont_len = pb->max_samples - pb->current_head; + + /* Don't copy more than we have */ + if (cont_len > count) + cont_len = count; + + /* Copy samples */ + memcpy(pb->current_ua + pb->current_head, current_ua, + sizeof(pb->current_ua[0]) * cont_len); + memcpy(pb->mab + pb->current_head, mab, + sizeof(pb->mab[0]) * cont_len); + pb->current_head = (pb->current_head + + cont_len) % pb->max_samples; + + /* Advance source pointers and count */ + current_ua += cont_len; + mab += cont_len; + count -= cont_len; + } +} + +address_t powerbuf_last_mab(powerbuf_t pb) +{ + const struct powerbuf_session *s = &pb->sessions[rev_index(pb, 0)]; + const unsigned int last = (pb->current_head + pb->max_samples - 1) % + pb->max_samples; + + if (s->start_index == pb->current_head) + return 0; + + return pb->mab[last]; +} diff --git a/util/powerbuf.h b/util/powerbuf.h new file mode 100644 index 0000000..d674d15 --- /dev/null +++ b/util/powerbuf.h @@ -0,0 +1,123 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2012 Daniel Beer + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef POWERBUF_H_ +#define POWERBUF_H_ + +/* This header file describes a data structure for recording power + * profiling data. + * + * Power profile data consists of zero or more discontiguous "sessions". + * Within each session is a sequence of evenly spaced current samples + * and the corresponding MAB values. + */ + +#include +#include "util.h" + +/* Per-session information header. */ +struct powerbuf_session { + /* Time that this session started. */ + time_t wall_clock; + + /* Index of first sample in sample buffer corresponding to this + * session. + */ + unsigned int start_index; + + /* Integral of current consumed over this session. */ + unsigned long long total_ua; +}; + +#define POWERBUF_MAX_SESSIONS 8 +#define POWERBUF_DEFAULT_SAMPLES 131072 + +/* Power buffer data structure. The power buffer contains three circular + * buffers, two of which are dynamically allocated. Helper functions are + * provided for managing access. + */ +struct powerbuf { + /* These parameters are set at the time of construction and + * shouldn't be modified. + */ + unsigned int interval_us; + unsigned int max_samples; + + /* Session circular buffer. New sessions are created by + * overwriting the head and advancing it. Old sessions drop out + * the end of the buffer. + */ + struct powerbuf_session sessions[POWERBUF_MAX_SESSIONS]; + unsigned int session_head; + unsigned int session_tail; + + /* Sample circular buffer. A single head/tail pair indicates the + * extent of both the current and MAB samples, since they are + * synchronous with respect to one another. + * + * Adding samples to the buffer causes old samples to fall out + * the tail end. If enough samples are pushed, old sessions will + * also drop out above. + */ + unsigned int *current_ua; + address_t *mab; + unsigned int current_head; + unsigned int current_tail; +}; + +typedef struct powerbuf *powerbuf_t; + +/* Allocate/destroy a power buffer. The number of samples is fixed and + * can't change over the lifetime of the buffer. + */ +powerbuf_t powerbuf_new(unsigned int max_samples, unsigned int interval_us); +void powerbuf_free(powerbuf_t pb); + +/* Clear all sessions and samples from the buffer. */ +void powerbuf_clear(powerbuf_t pb); + +/* Begin a new session. This may cause an old session to drop out the + * end of the buffer. If the current session is empty, it will be + * overwritten. + * + * powerbuf_end_session() simply discards any empty session previously + * created by powerbuf_begin_session(). + */ +void powerbuf_begin_session(powerbuf_t pb, time_t when); +void powerbuf_end_session(powerbuf_t pb); + +/* This interface provides a convenient way of accessing the session + * circular buffer. Rather than using direct indices, we present an + * interface that mimics a flat array. Indices (rev_idx) start at 0, + * with 0 being the index of the most recent session. + */ +unsigned int powerbuf_num_sessions(powerbuf_t pb); +const struct powerbuf_session *powerbuf_session_info(powerbuf_t pb, + unsigned int rev_idx, unsigned int *length); + +/* Push samples into the buffer. The number of elements in both the + * current_ua and mab arrays is given by the parameter "count". + */ +void powerbuf_add_samples(powerbuf_t pb, unsigned int count, + const unsigned int *current_ua, + const address_t *mab); + +/* Retrieve the last known MAB for this session, or 0 if none exists. */ +address_t powerbuf_last_mab(powerbuf_t pb); + +#endif