BeRTOS
|
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 /* 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 00165 void timer_add(Timer *timer) 00166 { 00167 ATOMIC(timer_addToList(timer, &timers_queue)); 00168 } 00169 00176 Timer *timer_abort(Timer *timer) 00177 { 00178 ATOMIC(REMOVE(&timer->link)); 00179 DB(timer->magic = TIMER_MAGIC_INACTIVE;) 00180 00181 return timer; 00182 } 00183 00184 00185 INLINE void timer_poll(List *queue) 00186 { 00187 Timer *timer; 00188 00189 /* 00190 * Check the first timer request in the list and process 00191 * it when it has expired. Repeat this check until the 00192 * first node has not yet expired. Since the list is sorted 00193 * by expiry time, all the following requests are guaranteed 00194 * to expire later. 00195 */ 00196 while ((timer = (Timer *)LIST_HEAD(queue))->link.succ) 00197 { 00198 /* This request in list has not yet expired? */ 00199 if (timer_clock() - timer->tick < 0) 00200 break; 00201 00202 /* Retreat the expired timer */ 00203 REMOVE(&timer->link); 00204 DB(timer->magic = TIMER_MAGIC_INACTIVE;) 00205 00206 /* Execute the associated event */ 00207 event_do(&timer->expire); 00208 } 00209 } 00210 00215 void synctimer_add(Timer *timer, List *queue) 00216 { 00217 timer_addToList(timer, queue); 00218 } 00219 00237 void synctimer_poll(List *queue) 00238 { 00239 timer_poll(queue); 00240 } 00241 00242 #endif /* CONFIG_TIMER_EVENTS */ 00243 00244 00250 void timer_delayTicks(ticks_t delay) 00251 { 00252 /* We shouldn't sleep with interrupts disabled */ 00253 IRQ_ASSERT_ENABLED(); 00254 00255 #if CONFIG_KERN_SIGNALS 00256 Timer t; 00257 DB(t.magic = TIMER_MAGIC_INACTIVE;) 00258 if (proc_preemptAllowed()) 00259 { 00260 timer_setEvent(&t); 00261 timer_setDelay(&t, delay); 00262 timer_add(&t); 00263 timer_waitEvent(&t); 00264 } 00265 else 00266 #endif /* !CONFIG_KERN_SIGNALS */ 00267 { 00268 ticks_t start = timer_clock(); 00269 00270 /* Busy wait */ 00271 while (timer_clock() - start < delay) 00272 cpu_relax(); 00273 } 00274 } 00275 00276 00277 #if CONFIG_TIMER_UDELAY 00278 00285 void timer_busyWait(hptime_t delay) 00286 { 00287 hptime_t now, prev = timer_hw_hpread(); 00288 hptime_t delta; 00289 00290 for (;;) 00291 { 00292 now = timer_hw_hpread(); 00293 /* 00294 * The timer counter may wrap here and "prev" can become 00295 * greater than "now". So, be sure to always evaluate a 00296 * coherent timer difference: 00297 * 00298 * 0 prev now TIMER_HW_CNT 00299 * |_____|_______________|_____| 00300 * ^^^^^^^^^^^^^^^ 00301 * delta = now - prev 00302 * 00303 * 0 now prev TIMER_HW_CNT 00304 * |_____|_______________|_____| 00305 * ^^^^^ ^^^^^ 00306 * delta = (TIMER_HW_CNT - prev) + now 00307 * 00308 * NOTE: TIMER_HW_CNT can be any value, not necessarily a power 00309 * of 2. For this reason the "%" operator is not suitable for 00310 * the generic case. 00311 */ 00312 delta = (now < prev) ? ((hptime_t)TIMER_HW_CNT - prev + now) : 00313 (now - prev); 00314 if (delta >= delay) 00315 break; 00316 delay -= delta; 00317 prev = now; 00318 } 00319 } 00320 00328 void timer_delayHp(hptime_t delay) 00329 { 00330 if (UNLIKELY(delay > us_to_hptime(1000))) 00331 { 00332 timer_delayTicks(delay / (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC)); 00333 delay %= (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC); 00334 } 00335 00336 timer_busyWait(delay); 00337 } 00338 #endif /* CONFIG_TIMER_UDELAY */ 00339 00344 DEFINE_TIMER_ISR 00345 { 00346 /* 00347 * With the Metrowerks compiler, the only way to force the compiler generate 00348 * an interrupt service routine is to put a pragma directive within the function 00349 * body. 00350 */ 00351 #ifdef __MWERKS__ 00352 #pragma interrupt saveall 00353 #endif 00354 00355 /* 00356 * On systems sharing IRQ line and vector, this check is needed 00357 * to ensure that IRQ is generated by timer source. 00358 */ 00359 if (!timer_hw_triggered()) 00360 return; 00361 00362 TIMER_STROBE_ON; 00363 00364 /* Update the master ms counter */ 00365 ++_clock; 00366 00367 /* Update the current task's quantum (if enabled). */ 00368 proc_decQuantum(); 00369 00370 #if CONFIG_TIMER_EVENTS 00371 timer_poll(&timers_queue); 00372 #endif 00373 00374 /* Perform hw IRQ handling */ 00375 timer_hw_irq(); 00376 00377 TIMER_STROBE_OFF; 00378 } 00379 00380 MOD_DEFINE(timer) 00381 00382 00385 void timer_init(void) 00386 { 00387 #if CONFIG_KERN_IRQ 00388 MOD_CHECK(irq); 00389 #endif 00390 00391 #if CONFIG_TIMER_EVENTS 00392 LIST_INIT(&timers_queue); 00393 #endif 00394 00395 TIMER_STROBE_INIT; 00396 00397 _clock = 0; 00398 00399 timer_hw_init(); 00400 00401 MOD_INIT(timer); 00402 } 00403 00404 00405 #if (ARCH & ARCH_EMUL) 00406 00409 void timer_cleanup(void) 00410 { 00411 MOD_CLEANUP(timer); 00412 00413 timer_hw_cleanup(); 00414 00415 // Hmmm... apparently, the demo app does not cleanup properly 00416 //ASSERT(LIST_EMPTY(&timers_queue)); 00417 } 00418 #endif /* ARCH_EMUL */