summaryrefslogtreecommitdiffstats
path: root/main/common/timestuff.c
diff options
context:
space:
mode:
authorAmar Takhar <amar@rtems.org>2015-04-16 15:26:21 -0400
committerAmar Takhar <amar@rtems.org>2015-04-16 15:26:21 -0400
commit87db514f2dfff5ad67863a30f075b718706de346 (patch)
tree31edc1b1700de9f3d4825822534928f8a213d53f /main/common/timestuff.c
downloadumon-87db514f2dfff5ad67863a30f075b718706de346.tar.bz2
Initial commit of the umon repository.
Prior to this three changes were made: * Remove umon_ prefix from parent directories. * Collapse main/target/ into main/ * Remove ports/template/flashtest.scr.ucon script.
Diffstat (limited to 'main/common/timestuff.c')
-rwxr-xr-xmain/common/timestuff.c386
1 files changed, 386 insertions, 0 deletions
diff --git a/main/common/timestuff.c b/main/common/timestuff.c
new file mode 100755
index 0000000..5cab2d3
--- /dev/null
+++ b/main/common/timestuff.c
@@ -0,0 +1,386 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2013 Alcatel-Lucent
+ *
+ * Alcatel Lucent licenses this file to You under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. A copy of the License is contained the
+ * file LICENSE at the top level of this repository.
+ * You may also 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.
+ *
+ **************************************************************************
+ *
+ * timestuff.c
+ *
+ * These functions support the monitor's ability to deal with elapsed
+ * time on targets with or without hardware-timer support.
+ *
+ * The INCLUDE_HWTMR definition in config.h determines which mode
+ * the timer will run in.
+ *
+ * The monitor does not require any hardware-assist for maintaining
+ * elapsed time. With no hardware assist (INCLUDE_HWTMR set to 0),
+ * the value of LOOPS_PER_SECOND must be established in config.h, and
+ * can be calibrated using the '-c' option in the sleep command. Even
+ * with this calibration the accuracy of this mechanism is limited;
+ * however, since there is no code in the monitor that really requires
+ * extremely accurate timing this may be ok.
+ *
+ * On the other hand, it is preferrable to have accuracy, so on targets
+ * that have a pollable clock, the hooks can be put in place to allow
+ * that clock to be used as the hardware assist for the monitor's
+ * elapsed time measurements. The INCLUDE_HWTMR is set to 1, and
+ * TIMER_TICKS_PER_MSEC defines the number of ticks of the timer that
+ * correspond to 1 millisecond of elapsed time. The function target_timer()
+ * is assumed to be established and must return an unsigned long value
+ * that is the content of the polled hardware timer.
+ *
+ * Regardless of the hardware-assist or not, the following interface is
+ * used by the code in the monitor...
+ *
+ * #include "timer.h"
+ *
+ * struct elapsed_tmr tmr;
+ *
+ * startElapsedTimer(&tmr,TIMEOUT):
+ * do {
+ * SOMETHING();
+ * } while(!msecElapsed(&tmr));
+ *
+ * Refer to the functions startElapsedTimer() and msecElapsed() below for
+ * more details.
+ *
+ * Original author: Ed Sutter (ed.sutter@alcatel-lucent.com)
+ *
+ */
+
+#include "config.h"
+#include "stddefs.h"
+#include "cli.h"
+#include "genlib.h"
+#include "ether.h"
+#include "timer.h"
+#include "cpuio.h"
+
+/* startElapsedTimer() & msecElapsed():
+ * The timer is started by loading the values timeout_low and timeout_high
+ * with the number of ticks that must elapse for the timer to expire.
+ *
+ * In the case of the non-hardware-assisted timer, the expiration count
+ * is based on the value of LoopsPerMillisecond (derived from the
+ * LOOPS_PER_SECOND definition in config.h). Each time msecElapsed()
+ * is called the elapsed count is incremented until it exceeds the timeout
+ * timeout_low timeout_high values recorded by startElapsedTimer().
+ *
+ * The case of the hardware-assisted timer is similar except that now
+ * the number of ticks initialized in timeout_low and timeout_high are
+ * based on the tick rate of the hardware timer (TIMER_TICKS_PER_MSEC).
+ * This value is expected to be set in config.h. Each time msecElapsed()
+ * is called, it samples the timer and adds to the running total of ticks
+ * until it matches or exceeds the timeout_low and timeout_high values.
+ *
+ * Notice that 64-bit values are used (high & low) because a 32-bit value
+ * isn't large enough to deal with the tick rates (per second) of various
+ * CPUs.
+ */
+void
+startElapsedTimer(struct elapsed_tmr *tmr, long milliseconds)
+{
+ unsigned long new_tm_low;
+ unsigned long stepmsecs, stepticks, remainder;
+
+#if INCLUDE_HWTMR
+ tmr->tmrflags = HWTMR_ENABLED;
+ tmr->tpm = (unsigned long)TIMER_TICKS_PER_MSEC;
+#else
+ tmr->tmrflags = 0;
+ tmr->tpm = (unsigned long)LoopsPerMillisecond;
+#endif
+
+ tmr->elapsed_low = 0;
+ tmr->elapsed_high = 0;
+ tmr->timeout_high = 0;
+ tmr->timeout_low = 0;
+
+ /* Convert incoming timeout from a millisecond count to a
+ * tick count...
+ * Maximum number of milliseconds and ticks before 32-bit
+ * (tick counter) unsigned long overlaps
+ */
+ stepmsecs = 0xffffffff / tmr->tpm;
+ stepticks = stepmsecs * tmr->tpm;
+ remainder = (milliseconds % stepmsecs);
+
+ /* Take care of the step remainder
+ */
+ tmr->timeout_low = remainder * tmr->tpm;
+ milliseconds -= remainder;
+
+ for (;milliseconds; milliseconds -= stepmsecs) {
+ new_tm_low = tmr->timeout_low + stepticks;
+
+ if (new_tm_low < tmr->timeout_low)
+ tmr->timeout_high++;
+ tmr->timeout_low = new_tm_low;
+ }
+
+#if INCLUDE_HWTMR
+ tmr->tmrval = target_timer();
+#else
+ tmr->tmrval = 0;
+#endif
+
+}
+
+int
+msecElapsed(struct elapsed_tmr *tmr)
+{
+ ulong new_elapsed_low, new_tmrval, elapsed;
+
+ /* If timeout has already occurred, then we can assume that this
+ * function being called without a matching startElapsedTimer() call.
+ */
+ if (ELAPSED_TIMEOUT(tmr))
+ return(1);
+
+#if INCLUDE_HWTMR
+ new_tmrval = target_timer();
+#else
+ new_tmrval = tmr->tmrval + 1;
+#endif
+
+ /* Record how many ticks elapsed since the last call to msecElapsed
+ * and add that value to the total number of ticks that have elapsed.
+ */
+ elapsed = new_tmrval - tmr->tmrval;
+ new_elapsed_low = tmr->elapsed_low + elapsed;
+
+ if (new_elapsed_low < tmr->elapsed_low)
+ tmr->elapsed_high++;
+
+ /* If the total elapsed number of ticks exceeds the timeout number
+ * of ticks, then we can return 1 to indicate that the requested
+ * amount of time has elapsed. Otherwise, we record the values and
+ * return 0.
+ */
+ if ((tmr->elapsed_high >= tmr->timeout_high) &&
+ (new_elapsed_low >= tmr->timeout_low)) {
+ tmr->tmrflags |= TIMEOUT_OCCURRED;
+ return(1);
+ }
+
+ tmr->tmrval = new_tmrval;
+ tmr->elapsed_low = new_elapsed_low;
+ return(0);
+}
+
+/* msecRemainging():
+ * Used to query how many milliseconds were left (if any) in the timeout.
+ */
+ulong
+msecRemaining(struct elapsed_tmr *tmr)
+{
+ ulong high, low, msectot, leftover, divisor;
+
+ if (ELAPSED_TIMEOUT(tmr))
+ return(0);
+
+ high = tmr->timeout_high - tmr->elapsed_high;
+ low = tmr->timeout_low - tmr->elapsed_low;
+
+ msectot = leftover = 0;
+
+#if INCLUDE_HWTMR
+ divisor = (ulong)TIMER_TICKS_PER_MSEC;
+#else
+ divisor = (ulong)LoopsPerMillisecond;
+#endif
+
+ while(1) {
+ while (low > divisor) {
+ msectot++;
+ low -= divisor;
+ }
+ leftover += low;
+ if (high == 0)
+ break;
+ else {
+ high--;
+ low = 0xffffffff;
+ }
+ }
+
+ while(leftover > divisor) {
+ msectot++;
+ low -= divisor;
+ }
+ return(msectot);
+}
+
+/* monDelay():
+ * Delay for specified number of milliseconds.
+ * Refer to msecElapsed() description for a discussion on the
+ * accuracy of this delay.
+ */
+void
+monDelay(int milliseconds)
+{
+ struct elapsed_tmr tmr;
+
+ startElapsedTimer(&tmr,milliseconds);
+ while(!msecElapsed(&tmr)) {
+ WATCHDOG_MACRO;
+ pollethernet();
+ }
+}
+
+/* monTimer():
+ * Provide the API with the ability to start a millisecond-granularity
+ * timer with some countdown value, and poll it waiting for completion.
+ */
+int
+monTimer(int cmd, void *arg)
+{
+ int rc = 0;
+ struct elapsed_tmr *tmr = (struct elapsed_tmr *)arg;
+
+ switch(cmd) {
+ case TIMER_START:
+ startElapsedTimer(tmr,tmr->start);
+ break;
+ case TIMER_ELAPSED:
+ msecElapsed(tmr);
+ if (ELAPSED_TIMEOUT(tmr))
+ rc = 1;
+ break;
+ case TIMER_QUERY:
+#if INCLUDE_HWTMR
+ tmr->tmrflags = HWTMR_ENABLED;
+ tmr->tpm = (unsigned long)TIMER_TICKS_PER_MSEC;
+ tmr->currenttmrval = target_timer();
+#else
+ tmr->tmrflags = 0;
+ tmr->tpm = (unsigned long)LoopsPerMillisecond;
+ tmr->currenttmrval = 0;
+#endif
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+ return(rc);
+}
+
+#if INCLUDE_TFSSCRIPT
+
+/* Sleep():
+ * Simple delay loop accessible by the command line.
+ * This loop count is dependent on the underlying hardware.
+ * The LoopsPerMillisecond count is loaded with a default at startup, or
+ * it can be calibrated by the user via the -c option.
+ * Note that this LoopsPerMillisecond value is used in a few other places
+ * in the monitor also for time-dependent stuff.
+ * NOTES:
+ * - This is obviously not real accurate (not intended to be), but allows the
+ * monitor to be independent of the underlying hardware.
+ * - The delay time is very dependent on ethernet activity, since the call
+ * to pollethernet() part of the loop.
+ */
+
+
+char *SleepHelp[] = {
+ "Second or msec delay (not precise)",
+ "-[clmv:] {count}",
+#if INCLUDE_VERBOSEHELP
+ " -c calibrate new LPS count",
+ " -l store LPS count",
+ " -m millisecond",
+ " -v {LPSvarname}",
+#endif
+ 0,
+};
+
+int
+Sleep(int argc,char *argv[])
+{
+ int opt, calibrate, count, multiplier;
+
+ multiplier = 1000;
+ calibrate = 0;
+ while ((opt=getopt(argc,argv,"clmv:")) != -1) {
+ switch(opt) {
+ case 'c':
+ calibrate = 2;
+ break;
+ case 'l':
+ calibrate = 1;
+ break;
+ case 'm':
+ multiplier = 1;
+ break;
+ case 'v':
+ shell_sprintf(optarg,"%d",LoopsPerMillisecond*1000);
+ return(CMD_SUCCESS);
+ default:
+ return(CMD_PARAM_ERROR);
+ }
+ }
+
+ /* If no args, just print the current LPS value and return... */
+ if (argc == 1) {
+#if INCLUDE_HWTMR
+ printf("Hardware-based timer, LPS not applicable\n");
+#else
+ printf("Current LPS = %ld\n",LoopsPerMillisecond * 1000);
+#endif
+ return(CMD_SUCCESS);
+ }
+
+ /* For calibration, take in the count on the command line, then use
+ * it to put out 5 dots dot at the rate of the loop to allow the user
+ * to adjust it to be about 1 second.
+ */
+ if (calibrate) {
+#if INCLUDE_HWTMR
+ printf("Hardware-based timer, doesn't calibrate\n");
+#else
+ long lps;
+
+ if (argc != optind+1)
+ return(CMD_PARAM_ERROR);
+
+ printf("Current LPS: %ld\n",LoopsPerMillisecond * 1000);
+ lps = strtol(argv[optind],0,0);
+ LoopsPerMillisecond = lps/1000;
+ printf("New LPS: %ld%s\n",LoopsPerMillisecond * 1000,
+ lps % 1000 ? " (truncated by 1000)" : "");
+
+ if (calibrate == 2) {
+ count = 10;
+ while(count-- > 0) {
+ monDelay(1000);
+ putstr(".\007");
+ }
+ putchar('\n');
+ }
+#endif
+ return(CMD_SUCCESS);
+ }
+
+ if (argc == optind)
+ count = 1;
+ else
+ count = strtol(argv[optind],(char **)0,0);
+
+ monDelay(count * multiplier);
+ return(CMD_SUCCESS);
+}
+#endif