timer.c

Go to the documentation of this file.
00001 
00039 #include "timer.h"
00040 
00041 #include "cfg/cfg_timer.h"
00042 #include "cfg/cfg_wdt.h"
00043 #include "cfg/cfg_kern.h"
00044 #include <cfg/os.h>
00045 #include <cfg/debug.h>
00046 #include <cfg/module.h>
00047 
00048 #include <cpu/attr.h>
00049 #include <cpu/types.h>
00050 #include <cpu/irq.h>
00051 
00052 /*
00053  * Include platform-specific binding code if we're hosted.
00054  * Try the CPU specific one for bare-metal environments.
00055  */
00056 #if OS_HOSTED
00057     //#include OS_CSOURCE(timer)
00058     #include <emul/timer_posix.c>
00059 #else
00060     #include CPU_CSOURCE(timer)
00061 #endif
00062 
00063 /*
00064  * Sanity check for config parameters required by this module.
00065  */
00066 #if !defined(CONFIG_KERN) || ((CONFIG_KERN != 0) && CONFIG_KERN != 1)
00067     #error CONFIG_KERN must be set to either 0 or 1 in config.h
00068 #endif
00069 #if !defined(CONFIG_WATCHDOG) || ((CONFIG_WATCHDOG != 0) && CONFIG_WATCHDOG != 1)
00070     #error CONFIG_WATCHDOG must be set to either 0 or 1 in config.h
00071 #endif
00072 
00073 #if CONFIG_WATCHDOG
00074     #include <drv/wdt.h>
00075 #endif
00076 
00077 #if defined (CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
00078     #include <kern/signal.h> /* sig_wait(), sig_check() */
00079     #include <kern/proc.h>   /* proc_current() */
00080     #include <cfg/macros.h>  /* BV() */
00081 #endif
00082 
00083 
00094 #if !defined(CONFIG_TIMER_STROBE) || !CONFIG_TIMER_STROBE
00095     #define TIMER_STROBE_ON    do {/*nop*/} while(0)
00096     #define TIMER_STROBE_OFF   do {/*nop*/} while(0)
00097     #define TIMER_STROBE_INIT  do {/*nop*/} while(0)
00098 #endif
00099 
00100 
00102 volatile ticks_t _clock;
00103 
00104 
00105 #if CONFIG_TIMER_EVENTS
00106 
00110 REGISTER static List timers_queue;
00111 
00112 
00120 void timer_add(Timer *timer)
00121 {
00122     Timer *node;
00123     cpu_flags_t flags;
00124 
00125 
00126     /* Inserting timers twice causes mayhem. */
00127     ASSERT(timer->magic != TIMER_MAGIC_ACTIVE);
00128     DB(timer->magic = TIMER_MAGIC_ACTIVE;)
00129 
00130     IRQ_SAVE_DISABLE(flags);
00131 
00132     /* Calculate expiration time for this timer */
00133     timer->tick = _clock + timer->_delay;
00134 
00135     /*
00136      * Search for the first node whose expiration time is
00137      * greater than the timer we want to add.
00138      */
00139     node = (Timer *)LIST_HEAD(&timers_queue);
00140     while (node->link.succ)
00141     {
00142         /*
00143          * Stop just after the insertion point.
00144          * (this fancy compare takes care of wrap-arounds).
00145          */
00146         if (node->tick - timer->tick > 0)
00147             break;
00148 
00149         /* Go to next node */
00150         node = (Timer *)node->link.succ;
00151     }
00152 
00153     /* Enqueue timer request into the list */
00154     INSERT_BEFORE(&timer->link, &node->link);
00155 
00156     IRQ_RESTORE(flags);
00157 }
00158 
00159 
00166 Timer *timer_abort(Timer *timer)
00167 {
00168     ATOMIC(REMOVE(&timer->link));
00169     DB(timer->magic = TIMER_MAGIC_INACTIVE;)
00170 
00171     return timer;
00172 }
00173 
00174 #endif /* CONFIG_TIMER_EVENTS */
00175 
00176 
00180 void timer_delayTicks(ticks_t delay)
00181 {
00182     /* We shouldn't sleep with interrupts disabled */
00183     IRQ_ASSERT_ENABLED();
00184 
00185 #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
00186     Timer t;
00187 
00188     ASSERT(!sig_check(SIG_SINGLE));
00189     timer_setSignal(&t, proc_current(), SIG_SINGLE);
00190     timer_setDelay(&t, delay);
00191     timer_add(&t);
00192     sig_wait(SIG_SINGLE);
00193 
00194 #else /* !CONFIG_KERN_SIGNALS */
00195 
00196     ticks_t start = timer_clock();
00197 
00198     /* Busy wait */
00199     while (timer_clock() - start < delay)
00200     {
00201 #if CONFIG_WATCHDOG
00202         wdt_reset();
00203 #endif
00204     }
00205 
00206 #endif /* !CONFIG_KERN_SIGNALS */
00207 }
00208 
00209 
00210 #if CONFIG_TIMER_UDELAY
00211 
00218 void timer_busyWait(hptime_t delay)
00219 {
00220     hptime_t now, prev = timer_hw_hpread();
00221     hptime_t delta;
00222 
00223     for(;;)
00224     {
00225         now = timer_hw_hpread();
00226         /*
00227          * We rely on hptime_t being unsigned here to
00228          * reduce the modulo to an AND in the common
00229          * case of TIMER_HW_CNT.
00230          */
00231         delta = (now - prev) % TIMER_HW_CNT;
00232         if (delta >= delay)
00233             break;
00234         delay -= delta;
00235         prev = now;
00236     }
00237 }
00238 
00246 void timer_delayHp(hptime_t delay)
00247 {
00248     if (UNLIKELY(delay > us_to_hptime(1000)))
00249     {
00250         timer_delayTicks(delay / (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC));
00251         delay %= (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC);
00252     }
00253 
00254     timer_busyWait(delay);
00255 }
00256 #endif /* CONFIG_TIMER_UDELAY */
00257 
00258 
00263 DEFINE_TIMER_ISR
00264 {
00265     /*
00266      * With the Metrowerks compiler, the only way to force the compiler generate
00267      * an interrupt service routine is to put a pragma directive within the function
00268      * body.
00269      */
00270     #ifdef __MWERKS__
00271     #pragma interrupt saveall
00272     #endif
00273 
00274 #if CONFIG_TIMER_EVENTS
00275     Timer *timer;
00276 #endif
00277 
00278     /*
00279      * On systems sharing IRQ line and vector, this check is needed
00280      * to ensure that IRQ is generated by timer source.
00281      */
00282     if (!timer_hw_triggered())
00283         return;
00284 
00285     TIMER_STROBE_ON;
00286 
00287     /* Perform hw IRQ handling */
00288     timer_hw_irq();
00289 
00290     /* Update the master ms counter */
00291     ++_clock;
00292 
00293 #if CONFIG_TIMER_EVENTS
00294     /*
00295      * Check the first timer request in the list and process
00296      * it when it has expired. Repeat this check until the
00297      * first node has not yet expired. Since the list is sorted
00298      * by expiry time, all the following requests are guaranteed
00299      * to expire later.
00300      */
00301     while ((timer = (Timer *)LIST_HEAD(&timers_queue))->link.succ)
00302     {
00303         /* This request in list has not yet expired? */
00304         if (_clock - timer->tick < 0)
00305             break;
00306 
00307         /* Retreat the expired timer */
00308         REMOVE(&timer->link);
00309         DB(timer->magic = TIMER_MAGIC_INACTIVE;)
00310 
00311         /* Execute the associated event */
00312         event_do(&timer->expire);
00313     }
00314 #endif /* CONFIG_TIMER_EVENTS */
00315 
00316     TIMER_STROBE_OFF;
00317 }
00318 
00319 MOD_DEFINE(timer)
00320 
00321 
00324 void timer_init(void)
00325 {
00326     #if CONFIG_KERN_IRQ
00327         MOD_CHECK(irq);
00328     #endif
00329 
00330     #if CONFIG_TIMER_EVENTS
00331         LIST_INIT(&timers_queue);
00332     #endif
00333 
00334     TIMER_STROBE_INIT;
00335 
00336     _clock = 0;
00337 
00338     timer_hw_init();
00339 
00340     MOD_INIT(timer);
00341 }
00342 
00343 
00344 #if (ARCH & ARCH_EMUL)
00345 
00348 void timer_cleanup(void)
00349 {
00350     MOD_CLEANUP(timer);
00351 
00352     timer_hw_cleanup();
00353 
00354     // Hmmm... apparently, the demo app does not cleanup properly
00355     //ASSERT(LIST_EMPTY(&timers_queue));
00356 }
00357 #endif /* ARCH_EMUL */