timer.c
Go to the documentation of this file.
00001
00039 #include "timer.h"
00040 #include "hw/hw_timer.h"
00041
00042 #include "cfg/cfg_timer.h"
00043 #include "cfg/cfg_wdt.h"
00044 #include "cfg/cfg_proc.h"
00045 #include "cfg/cfg_signal.h"
00046 #include <cfg/os.h>
00047 #include <cfg/debug.h>
00048 #include <cfg/module.h>
00049
00050 #include <cpu/attr.h>
00051 #include <cpu/types.h>
00052 #include <cpu/irq.h>
00053 #include <cpu/power.h> // cpu_relax()
00054
00055 #include <kern/proc_p.h> // proc_decQuantun()
00056
00057 /*
00058  * Include platform-specific binding code if we're hosted.
00059  * Try the CPU specific one for bare-metal environments.
00060  */
00061 #if OS_HOSTED
00062     //#include OS_CSOURCE(timer)
00063     #include <emul/timer_posix.c>
00064 #else
00065     #ifndef WIZ_AUTOGEN
00066         #warning Deprecated: now you should include timer_<cpu> directly in the makefile. Remove this line and the following once done.
00067         #include CPU_CSOURCE(timer)
00068     #endif
00069 #endif
00070 
00071 /*
00072  * Sanity check for config parameters required by this module.
00073  */
00074 #if !defined(CONFIG_KERN) || ((CONFIG_KERN != 0) && CONFIG_KERN != 1)
00075     #error CONFIG_KERN must be set to either 0 or 1 in config.h
00076 #endif
00077 #if !defined(CONFIG_WATCHDOG) || ((CONFIG_WATCHDOG != 0) && CONFIG_WATCHDOG != 1)
00078     #error CONFIG_WATCHDOG must be set to either 0 or 1 in config.h
00079 #endif
00080 
00081 #if CONFIG_WATCHDOG
00082     #include <drv/wdt.h>
00083 #endif
00084 
00085 #if defined (CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
00086     #include <kern/signal.h> /* sig_wait(), sig_check() */
00087     #include <kern/proc.h>   /* proc_current() */
00088     #include <cfg/macros.h>  /* BV() */
00089 #endif
00090 
00091
00102 #if !defined(CONFIG_TIMER_STROBE) || !CONFIG_TIMER_STROBE
00103     #define TIMER_STROBE_ON    do {/*nop*/} while(0)
00104     #define TIMER_STROBE_OFF   do {/*nop*/} while(0)
00105     #define TIMER_STROBE_INIT  do {/*nop*/} while(0)
00106 #endif
00107 
00108
00110 volatile ticks_t _clock;
00111
00112
00113 #if CONFIG_TIMER_EVENTS
00114 
00118 REGISTER static List timers_queue;
00119
00124 INLINE void timer_addToList(Timer *timer, List *queue)
00125 {
00126     /* Inserting timers twice causes mayhem. */
00127     ASSERT(timer->magic != TIMER_MAGIC_ACTIVE);
00128     DB(timer->magic = TIMER_MAGIC_ACTIVE;)
00129
00130     /*
00131      * Search for the first node whose expiration time is
00132      * greater than the timer we want to add.
00133      */
00134     Timer *node = (Timer *)LIST_HEAD(queue);
00135     while (node->link.succ)
00136     {
00137         /*
00138          * Stop just after the insertion point.
00139          * (this fancy compare takes care of wrap-arounds).
00140          */
00141         if (node->tick - timer->tick > 0)
00142             break;
00143
00144         /* Go to next node */
00145         node = (Timer *)node->link.succ;
00146     }
00147
00148     /* Enqueue timer request into the list */
00149     INSERT_BEFORE(&timer->link, &node->link);
00150 }
00151
00159 void timer_add(Timer *timer)
00160 {
00161     ATOMIC(
00162         /* Calculate expiration time for this timer */
00163         timer->tick = _clock + timer->_delay;
00164
00165         timer_addToList(timer, &timers_queue);
00166     );
00167 }
00168
00175 Timer *timer_abort(Timer *timer)
00176 {
00177     ATOMIC(REMOVE(&timer->link));
00178     DB(timer->magic = TIMER_MAGIC_INACTIVE;)
00179
00180     return timer;
00181 }
00182
00183
00184 INLINE void timer_poll(List *queue)
00185 {
00186     Timer *timer;
00187
00188     /*
00189      * Check the first timer request in the list and process
00190      * it when it has expired. Repeat this check until the
00191      * first node has not yet expired. Since the list is sorted
00192      * by expiry time, all the following requests are guaranteed
00193      * to expire later.
00194      */
00195     while ((timer = (Timer *)LIST_HEAD(queue))->link.succ)
00196     {
00197         /* This request in list has not yet expired? */
00198         if (timer_clock() - timer->tick < 0)
00199             break;
00200
00201         /* Retreat the expired timer */
00202         REMOVE(&timer->link);
00203         DB(timer->magic = TIMER_MAGIC_INACTIVE;)
00204
00205         /* Execute the associated event */
00206         event_do(&timer->expire);
00207     }
00208 }
00209
00214 void synctimer_add(Timer *timer, List *queue)
00215 {
00216     timer->tick = timer_clock() + timer->_delay;
00217
00218     timer_addToList(timer, queue);
00219 }
00220
00221 void synctimer_readd(Timer *timer, List *queue)
00222 {
00223     timer->tick += timer->_delay;
00224     timer_addToList(timer, queue);
00225 }
00226
00227
00245 void synctimer_poll(List *queue)
00246 {
00247     timer_poll(queue);
00248 }
00249
00250 #endif /* CONFIG_TIMER_EVENTS */
00251
00252
00258 void timer_delayTicks(ticks_t delay)
00259 {
00260     /* We shouldn't sleep with interrupts disabled */
00261     IRQ_ASSERT_ENABLED();
00262
00263 #if CONFIG_KERN_SIGNALS
00264     Timer t;
00265     DB(t.magic = TIMER_MAGIC_INACTIVE;)
00266     if (proc_preemptAllowed())
00267     {
00268         ASSERT(!sig_check(SIG_SINGLE));
00269         timer_setSignal(&t, proc_current(), SIG_SINGLE);
00270         timer_setDelay(&t, delay);
00271         timer_add(&t);
00272         sig_wait(SIG_SINGLE);
00273     }
00274     else
00275 #endif /* !CONFIG_KERN_SIGNALS */
00276     {
00277         ticks_t start = timer_clock();
00278
00279         /* Busy wait */
00280         while (timer_clock() - start < delay)
00281             cpu_relax();
00282     }
00283 }
00284
00285
00286 #if CONFIG_TIMER_UDELAY
00287 
00294 void timer_busyWait(hptime_t delay)
00295 {
00296     hptime_t now, prev = timer_hw_hpread();
00297     hptime_t delta;
00298
00299     for (;;)
00300     {
00301         now = timer_hw_hpread();
00302         /*
00303          * The timer counter may wrap here and "prev" can become
00304          * greater than "now". So, be sure to always evaluate a
00305          * coherent timer difference:
00306          *
00307          * 0     prev            now   TIMER_HW_CNT
00308          * |_____|_______________|_____|
00309          *        ^^^^^^^^^^^^^^^
00310          * delta = now - prev
00311          *
00312          * 0     now             prev  TIMER_HW_CNT
00313          * |_____|_______________|_____|
00314          *  ^^^^^                 ^^^^^
00315          * delta = (TIMER_HW_CNT - prev) + now
00316          *
00317          * NOTE: TIMER_HW_CNT can be any value, not necessarily a power
00318          * of 2. For this reason the "%" operator is not suitable for
00319          * the generic case.
00320          */
00321         delta = (now < prev) ? ((hptime_t)TIMER_HW_CNT - prev + now) :
00322                         (now - prev);
00323         if (delta >= delay)
00324             break;
00325         delay -= delta;
00326         prev = now;
00327     }
00328 }
00329
00337 void timer_delayHp(hptime_t delay)
00338 {
00339     if (UNLIKELY(delay > us_to_hptime(1000)))
00340     {
00341         timer_delayTicks(delay / (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC));
00342         delay %= (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC);
00343     }
00344
00345     timer_busyWait(delay);
00346 }
00347 #endif /* CONFIG_TIMER_UDELAY */
00348
00353 DEFINE_TIMER_ISR
00354 {
00355     /*
00356      * With the Metrowerks compiler, the only way to force the compiler generate
00357      * an interrupt service routine is to put a pragma directive within the function
00358      * body.
00359      */
00360     #ifdef __MWERKS__
00361     #pragma interrupt saveall
00362     #endif
00363 
00364     /*
00365      * On systems sharing IRQ line and vector, this check is needed
00366      * to ensure that IRQ is generated by timer source.
00367      */
00368     if (!timer_hw_triggered())
00369         return;
00370
00371     TIMER_STROBE_ON;
00372
00373     /* Update the master ms counter */
00374     ++_clock;
00375
00376     /* Update the current task's quantum (if enabled). */
00377     proc_decQuantum();
00378
00379     #if CONFIG_TIMER_EVENTS
00380         timer_poll(&timers_queue);
00381     #endif
00382 
00383     /* Perform hw IRQ handling */
00384     timer_hw_irq();
00385
00386     TIMER_STROBE_OFF;
00387 }
00388
00389 MOD_DEFINE(timer)
00390
00391
00394 void timer_init(void)
00395 {
00396     #if CONFIG_KERN_IRQ
00397         MOD_CHECK(irq);
00398     #endif
00399 
00400     #if CONFIG_TIMER_EVENTS
00401         LIST_INIT(&timers_queue);
00402     #endif
00403 
00404     TIMER_STROBE_INIT;
00405
00406     _clock = 0;
00407
00408     timer_hw_init();
00409
00410     MOD_INIT(timer);
00411 }
00412
00413
00414 #if (ARCH & ARCH_EMUL) || (CPU_ARM_AT91)
00415 
00418 void timer_cleanup(void)
00419 {
00420     MOD_CLEANUP(timer);
00421
00422     timer_hw_cleanup();
00423 }
00424 #endif