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/preempt.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     /* Calculate expiration time for this timer */
00132     timer->tick = _clock + timer->_delay;
00133 
00134     /*
00135      * Search for the first node whose expiration time is
00136      * greater than the timer we want to add.
00137      */
00138     Timer *node = (Timer *)LIST_HEAD(queue);
00139     while (node->link.succ)
00140     {
00141         /*
00142          * Stop just after the insertion point.
00143          * (this fancy compare takes care of wrap-arounds).
00144          */
00145         if (node->tick - timer->tick > 0)
00146             break;
00147 
00148         /* Go to next node */
00149         node = (Timer *)node->link.succ;
00150     }
00151 
00152     /* Enqueue timer request into the list */
00153     INSERT_BEFORE(&timer->link, &node->link);
00154 }
00155 
00163 void timer_add(Timer *timer)
00164 {
00165     ATOMIC(timer_addToList(timer, &timers_queue));
00166 }
00167 
00174 Timer *timer_abort(Timer *timer)
00175 {
00176     ATOMIC(REMOVE(&timer->link));
00177     DB(timer->magic = TIMER_MAGIC_INACTIVE;)
00178 
00179     return timer;
00180 }
00181 
00182 
00183 INLINE void timer_poll(List *queue)
00184 {
00185     Timer *timer;
00186 
00187     /*
00188      * Check the first timer request in the list and process
00189      * it when it has expired. Repeat this check until the
00190      * first node has not yet expired. Since the list is sorted
00191      * by expiry time, all the following requests are guaranteed
00192      * to expire later.
00193      */
00194     while ((timer = (Timer *)LIST_HEAD(queue))->link.succ)
00195     {
00196         /* This request in list has not yet expired? */
00197         if (timer_clock() - timer->tick < 0)
00198             break;
00199 
00200         /* Retreat the expired timer */
00201         REMOVE(&timer->link);
00202         DB(timer->magic = TIMER_MAGIC_INACTIVE;)
00203 
00204         /* Execute the associated event */
00205         event_do(&timer->expire);
00206     }
00207 }
00208 
00213 void synctimer_add(Timer *timer, List *queue)
00214 {
00215     timer_addToList(timer, queue);
00216 }
00217 
00235 void synctimer_poll(List *queue)
00236 {
00237     timer_poll(queue);
00238 }
00239 
00240 #endif /* CONFIG_TIMER_EVENTS */
00241 
00242 
00248 void timer_delayTicks(ticks_t delay)
00249 {
00250     /* We shouldn't sleep with interrupts disabled */
00251     IRQ_ASSERT_ENABLED();
00252 
00253 #if CONFIG_KERN_SIGNALS
00254     Timer t;
00255 
00256     if (proc_preemptAllowed())
00257     {
00258         ASSERT(!sig_check(SIG_SINGLE));
00259         timer_setSignal(&t, proc_current(), SIG_SINGLE);
00260         timer_setDelay(&t, delay);
00261         timer_add(&t);
00262         sig_wait(SIG_SINGLE);
00263     }
00264     else
00265 #endif /* !CONFIG_KERN_SIGNALS */
00266     {
00267         ticks_t start = timer_clock();
00268 
00269         /* Busy wait */
00270         while (timer_clock() - start < delay)
00271             cpu_relax();
00272     }
00273 }
00274 
00275 
00276 #if CONFIG_TIMER_UDELAY
00277 
00284 void timer_busyWait(hptime_t delay)
00285 {
00286     hptime_t now, prev = timer_hw_hpread();
00287     hptime_t delta;
00288 
00289     for(;;)
00290     {
00291         now = timer_hw_hpread();
00292         /*
00293          * We rely on hptime_t being unsigned here to
00294          * reduce the modulo to an AND in the common
00295          * case of TIMER_HW_CNT.
00296          */
00297         delta = (now - prev) % TIMER_HW_CNT;
00298         if (delta >= delay)
00299             break;
00300         delay -= delta;
00301         prev = now;
00302     }
00303 }
00304 
00312 void timer_delayHp(hptime_t delay)
00313 {
00314     if (UNLIKELY(delay > us_to_hptime(1000)))
00315     {
00316         timer_delayTicks(delay / (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC));
00317         delay %= (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC);
00318     }
00319 
00320     timer_busyWait(delay);
00321 }
00322 #endif /* CONFIG_TIMER_UDELAY */
00323 
00328 DEFINE_TIMER_ISR
00329 {
00330     /*
00331      * With the Metrowerks compiler, the only way to force the compiler generate
00332      * an interrupt service routine is to put a pragma directive within the function
00333      * body.
00334      */
00335     #ifdef __MWERKS__
00336     #pragma interrupt saveall
00337     #endif
00338 
00339     /*
00340      * On systems sharing IRQ line and vector, this check is needed
00341      * to ensure that IRQ is generated by timer source.
00342      */
00343     if (!timer_hw_triggered())
00344         return;
00345 
00346     TIMER_STROBE_ON;
00347 
00348     /* Perform hw IRQ handling */
00349     timer_hw_irq();
00350 
00351     /* Update the master ms counter */
00352     ++_clock;
00353 
00354     /* Update the current task's quantum (if enabled). */
00355     proc_decQuantum();
00356 
00357     #if CONFIG_TIMER_EVENTS
00358         timer_poll(&timers_queue);
00359     #endif
00360 
00361     TIMER_STROBE_OFF;
00362 }
00363 
00364 MOD_DEFINE(timer)
00365 
00366 
00369 void timer_init(void)
00370 {
00371     #if CONFIG_KERN_IRQ
00372         MOD_CHECK(irq);
00373     #endif
00374 
00375     #if CONFIG_TIMER_EVENTS
00376         LIST_INIT(&timers_queue);
00377     #endif
00378 
00379     TIMER_STROBE_INIT;
00380 
00381     _clock = 0;
00382 
00383     timer_hw_init();
00384 
00385     MOD_INIT(timer);
00386 }
00387 
00388 
00389 #if (ARCH & ARCH_EMUL)
00390 
00393 void timer_cleanup(void)
00394 {
00395     MOD_CLEANUP(timer);
00396 
00397     timer_hw_cleanup();
00398 
00399     // Hmmm... apparently, the demo app does not cleanup properly
00400     //ASSERT(LIST_EMPTY(&timers_queue));
00401 }
00402 #endif /* ARCH_EMUL */