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