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